
Why the Usage of Instrumentation Within Monitoring Tools Should be Implemented in Your Next Web Project
Back-EndWhen designing a web application, a strategy that has often been used is to use a monitoring tool such as Grafana or Datadog. There...
In parts one and two of this series, we set up a frontend and backend to view notifications from third-party services like GitHub, Netlify, and Heroku. It works like this:
Now our client is set up to view our messages, but we need to quit and restart the app to get any updates. We could add pull-to-refresh functionality, but it’d be much nicer if we could automatically receive updates from the server when a new message is received. Let’s build out WebSockets functionality to accomplish these live updates. Here’s an illustration of how the flow of data will work:
If you like, you can download the completed server project and the completed client project for part 3.
There are a few different libraries that can provide WebSocket functionality to Node apps. For the sake of this tutorial, we’ll use websocket
:
$ yarn add websocket
In our worker, after we handle a message on the incoming
 queue and save the message to the database, we’ll send a message out on another queue indicating that we should deliver that message over the WebSocket. We’ll call that new queue socket
. Make the following change in workers/index.js
:
const handleIncoming = message => repo .create(message) .then(record => { console.log('Saved ' + JSON.stringify(record)); + return queue.send('socket', record); }); queue .receive('incoming', handleIncoming)
Note the following sequence:
incoming
queue;socket
 queue.Note that we haven’t yet implemented the WebSocket code to send the response to the client yet; we’ll do that next. So far, we’ve just sent a message to a new queue that the WebSocket code will watch.
Now let’s implement the WebSocket code. In the web
 folder, create a file socket.js
 and add the following:
const WebSocketServer = require('websocket').server; const configureWebSockets = httpServer => { const wsServer = new WebSocketServer({ httpServer }); }; module.exports = configureWebSockets;
We create a function configureWebSockets
 that allows us to pass in a Node httpServer
 and creates a WebSocketServer
 from it.
Next, let’s add some boilerplate code to allow a client to establish a WebSocket connection:
const configureWebSockets = httpServer => { const wsServer = new WebSocketServer({ httpServer }); + + let connection; + + wsServer.on('request', function(request) { + connection = request.accept(null, request.origin); + console.log('accepted connection'); + + connection.on('close', function() { + console.log('closing connection'); + connection = null; + }); + }); };
All we do is save the connection
 in a variable and add a little logging to indicate when we’ve connected and disconnected. Note that our server is only allowing one connection; if a new one comes in, it’ll be overwritten. In a production application you would want to structure your code to handle multiple connections. Some WebSocket libraries will handle multiple connections for you.
Next, we want to listen on the socket
 queue we set up before, and send an outgoing message on our WebSocket connection when we get one:
const WebSocketServer = require('websocket').server; +const queue = require('../lib/queue'); const configureWebSockets = httpServer => { ... wsServer.on('request', function(request) { ... }); + + queue + .receive('socket', message => { + if (!connection) { + console.log('no WebSocket connection'); + return; + } + connection.sendUTF(JSON.stringify(message)); + }) + .catch(console.error); }
When a message is sent on the socket
queue and if there is no WebSocket client connection, we do nothing. If there is a WebSocket client connection we send the message we receive out over it.
Now, we just need to call our configureWebSockets
 function, passing our HTTP server to it. Open web/index.js
 and add the following:
const listRouter = require('./list'); +const configureWebSockets = require('./socket'); const app = express(); ... const server = http.createServer(app); +configureWebSockets(server);
By calling our function, which in turn calls new WebSocketServer()
, we enable our server to accept requests for WebSocket connections.
Now we need to update our Expo client to make that WebSocket connection to the backend and accept messages it sends, updating the screen in the process. On the frontend we don’t need to add a dependency to handle WebSockets; the WebSocket
 API is built-in to React Native’s JavaScript runtime.
Open src/MessageList.js
 and add the following:
const httpUrl = Platform.select({ ios: 'http://localhost:3000', android: 'http://10.0.2.2:3000', }); +const wsUrl = Platform.select({ + ios: 'ws://localhost:3000', + android: 'ws://10.0.2.2:3000', +}); + +let socket; + +const setUpWebSocket = addMessage => { + if (!socket) { + socket = new WebSocket(wsUrl); + console.log('Attempting Connection...'); + + socket.onopen = () => { + console.log('Successfully Connected'); + }; + + socket.onclose = event => { + console.log('Socket Closed Connection: ', event); + socket = null; + }; + + socket.onerror = error => { + console.log('Socket Error: ', error); + }; + } + + socket.onmessage = event => { + addMessage(JSON.parse(event.data)); + }; +}; const loadInitialData = async setMessages => {
This creates a function setUpWebSocket
 that ensures our WebSocket is ready to go. If the WebSocket is not already opened, it opens it and hooks up some logging. Whether or not it was already open, we configure the WebSocket to pass any message it receives along to the passed-in addMessage
 function.
Now, let’s call setUpWebSocket
 from our component function:
useEffect(() => { loadInitialData(setMessages); }, []); + useEffect(() => { + setUpWebSocket(newMessage => { + setMessages([newMessage, ...messages]); + }); + }, [messages]); + return ( <View style={{ flex: 1 }}>
We call setUpWebSocket
 in a useEffect
 hook. We pass it a function allowing it to append a new message to the state. This effect depends on the messages
 state.
As a result of these dependencies, when the messages
 are changed, we create a new addMessage
 callback that appends the message to the updated messages
array and then we call setUpWebsocket
 again with that updated addMessage
 callback. This is why we wrote setUpWebsocket
 to work whether or not the WebSocket is already established; it will be called multiple times.
With this, we’re ready to give our WebSockets a try! Make sure you have both Node services running in different terminals:
$ node web
$ node workers
Then reload our Expo app:
In yet another terminal, send in a new message:
$ curl http://localhost:3000/webhook -d "this is for WebSocketyness"
You should see the message appear in the Expo app right away, without any action needed by the user. We’ve got live updates!
Now that we’ve proven out that we can get live updates to our app, we should move beyond our simple webhook and get data from real third-party services. In the next part, we’ll set up a webhook to get notifications from GitHub about pull request events.
When designing a web application, a strategy that has often been used is to use a monitoring tool such as Grafana or Datadog. There...
There are several design patterns used these days in the .NET ecosystem. What are they? What are the benefits and drawbacks of each pattern?...
As a full-stack web developer, I attended SRECon to expand my thinking about the reliability and observability of the services I develop. Here are...