Stream tweets with React, Express, Socket.io and Twitter
Back in the dark days, setTimeouts helped to poll and maintain state between the front end and backend. Comparable to the industrial revolution, Socket.io revolutionized communication for real-time solutions, effectively removing the need to poll.
“Socket.IO enables real-time bidirectional event-based communication.It works on every platform, browser or device, focusing equally on reliability and speed.” — Socket.IO
In this post, let’s build a streaming solution to stream tweets as they are created. In the process, you will learn how to use socket.io to create real-time bidirectional communication.
The app you will be building –
Setup
It’s going to be a create-react-app. If you don’t have the module already installed, install creat-react-app globally.
npm install create-react-app -gcreate-react-app tweeties cd tweeties npm start
We are also going to be using:
- Express — to create APIs
- Twitter — to fetch streams
- socket.io — to establish bi-directional communication from the server
- socket.io-client — to latch on to the communication channel from the front end
- react-transition-group — to add animation when new tweets flow in
At the end of the installation, your package.json should look like –
{ "name": "tweeties", "version": "0.1.0", "private": true, "dependencies": { "body-parser": "^1.18.2", "react": "^16.2.0", "react-dom": "^16.2.0", "react-scripts": "1.1.1", "react-transition-group": "^1.2.1", "socket.io": "^2.0.4", "socket.io-client": "^2.0.4", "twitter": "^1.7.1", "express": "4.16.3" }, "proxy": "http://localhost:3001", "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test --env=jsdom", "eject": "react-scripts eject" }}
For the trained eye, you will be fixated on the “proxy” key. What is it? Let me explain, create-react-apps ‘npm start’ launches the dev server on port 3000; therefore, all requests from the front end which do not specify an alternate domain will hit port 3000. For example, the below request will hit http:localhost:3000/
fetch("/pause", {
method: "POST",
headers: {'Content-Type': 'application/json'
}
});
Now, react itself is not a framework but a view library. So, in order to host all our APIs, we create an express server at port 3001 and proxy all requests from 3000 to 3001. Makes sense? If it does not, leave a comment.
Server
Create another directory ‘server’ to host all backend code.
tweeties
├── README.md
├── node_modules
├── package.json
├── .gitignore
├── public
│ └── favicon.ico
│ └── index.html
│ └── manifest.json
└── src
├── server
└── server.js
└── App.css
└── App.js
└── App.test.js
└── index.css
└── index.js
└── logo.svg
└── registerServiceWorker.js
Like most express apps, require necessary modules and build up the server. If you are new to express, read up on getting started with express and come right back.
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const path = require('path');
const bodyParser = require('body-parser');
const port = process.env.PORT || 3001;
const app = express();const server = http.createServer(app);app.use(bodyParser.json());server.listen(port, () => {
console.log('server is up');
});
Before we move onto creating routes for our twitter stream, let’s get acquainted with twitter — the npm module for Twitter REST and Streaming API’s. To get started with the module, you need to first register your app with Twitter at https://apps.twitter.com/. Once you register, you will be presented with a consumer key, consumer secret, access token and access token secret. You can now create a new instance of the Twitter module like so –
let twitter = new Twitter({
consumer_key: process.env.TWITTER_CONSUMER_KEY,
consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
access_token_key: process.env.TWITTER_ACCESS_TOKEN_KEY,
access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
});
Socket.io usage is pretty straightforward. Create a socket.io connection by passing the previously created server instance to socket.io
const io = socketio(server);
Now you can listen when a connection is established or disconnected.
Your server.js should look like –
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const path = require('path');
const bodyParser = require('body-parser');
const port = process.env.PORT || 3001;
const app = express();const server = http.createServer(app);
const io = socketio(server);app.use(bodyParser.json());require('./routes/tweets.js')(app, io);server.listen(port, () => {
console.log('server is up');
});
Once you have the twitter instance and socket.io channel created, you are all set to emit twitter data as they are streamed to our backend.
//Establishes socket connection.
io.on("connection", socket => {
stream();
socket.on("connection", () => console.log("Client connected"));
socket.on("disconnect", () => console.log("Client disconnected"));
});//Emits data with socket.io as twitter stream flows in
const stream = () => {
twitter.stream('statuses/filter', { track: app.locals.searchTerm }, (stream) => {
stream.on('data', (tweet) => {
sendMessage(tweet);
});stream.on('error', (error) => {
console.log(error);
});
});
}
That is it! So far, we have created a web server which listens on twitter streams and emits received data so that listeners (typically on the front end) are kept informed as tweets are created real time.
Front end (React app)
First thing first, create a new React component ‘TweetList’ to consolidate view and logic related to tweets.
import React, { Component } from 'react';
import './App.css';
import TweetList from './components/TweetList';class App extends Component {
render() {
return (
<div className="App">
<TweetList/>
</div>
);
}
}export default App;
At the TweetList component, listen to incoming events by creating a socket.io connection at the componentDidMount lifecycle method –
componentDidMount() {
const socket = socketIOClient('http://localhost:3000/');socket.on('connect', () => {
console.log("Socket Connected");
socket.on("tweets", data => {
let newList = [data].concat(this.state.items);
this.setState({ items: newList });
});
});
socket.on('disconnect', () => {
socket.removeAllListeners("tweets");
console.log("Socket Disconnected");
});
}
Once the socket channel is connected, we listen on tweets, prepend them on our existing items array and setState. Once setState() is called, react handles mutating the DOM to the right representation. You can optionally use materialize-css to beautify the tweets.
To recap, you learned how to create an express server to retrieve streams as well as to create APIs. You also learned how to listen to socket.io events from a React application.
The full source code is available at GitHub. Once you clone it, be sure to provide your Twitter access tokens at src/server/routes/tweets.js
Socket.io is a great library to create bi-directional communication for real-time applications. It can be used for push notifications, multiplayer games, chat rooms and many more real-time solutions. If you haven’t tried it out yet, give it a go!
Originally published at javascriptstore.com on March 18, 2018.