Making A Lightning Web App: Part 3
Making a Lightning Fast UI with WebSockets
Hey, this is part 3 of a 5 part series on building a Lightning app. If you haven’t read parts 1 & 2, you might want to start from the beginning.
Our app as we left it in part 2 of the tutorial series is now a fully functioning Lightning web app, but it’s far from the polished result we’re aiming for. It relies on polling the server for updates, and makes the page refresh when we make a post. So we’re going to speed things up by replacing some of our traditional calls with Web Sockets.
WebSockets and Lightning make a great pair, as the ability to make small and frequent payments needs small and frequent messages to be sent back and forth between the client and the server. If you’re not familiar with how sockets work, I suggest doing a bit of light reading before we get started. But don’t worry, the concept is extremely simple once you see how it works, and I’ll try to explain along the way.
Getting Started: Clone the (new) Project
Yet again, we’ll start with cloning a further iteration of the project. If you want to keep any changes you made to the first part, feel free to manually add the new code.
# Run this if you don't already have the repository
git clone https://github.com/wbobeirne/lightning-app-tutorial.git
cd lightning-app-tutorial
git checkout part-3
# Run this inside the repository directory if you already had it
git fetch origin
git checkout part-3
Also yet again, you’ll need to install the dependencies we need. We didn’t add too many this go-around, but adding web sockets to our API has necessitated a few more:
npm install
Let’s move on to the code!
The Code
We haven’t added any new files here, we’ve just altered some of the previous ones to use websockets instead. Let’s start with the server:
server/index.ts
We’ve added a new websocket endpoint here using the express-ws
module. Unlike GET, POST, PUT, or DELETE requests that return information once as a part of an HTTP request, websocket endpoints open a persistent connection to the server that allow us to pass data back and forth. In this case though, we'll only be passing information down to the client, and otherwise continue using HTTP requests for everything else.
The first thing we do is grab all of our existing posts, and feed them through the socket. Each post is sent as a “message” of JSON encoded data. We then setup an event listener for new posts, and when they’re made, we send a message as they come in. This’ll make our application update in real time!
The final bits are just websocket house-keeping. We ping the client every 10 seconds just to keep the connection going, as some clients and servers will automatically close off sockets that haven’t sent message for a bit. And once the connection is closed, we’ll stop trying to send new posts through it.
And that’s it for changes to our server! Onto the client:
client/App.tsx
It looks like a lot of code, but much of it is component state boilerplate. We’ve now made our App
component the central store of posts
data by using its component state, and passing it down as props so that our other components can be much simpler.
As soon as our App
component mounts, it’ll open a websocket by calling this.connect()
in componentDidMount
. It saves incoming posts to component state, and passes them to the Posts
and PostForm
components as props. If there are any errors in the connection, we can save that too and display an error message along with a button to call the connect method again.
While our Posts
component is now so simple that it's only a render function, our PostsForm
component has been changed a bit to handle the data now coming in from props:
client/components/PostsForm.tsx
We’ve replaced our previous methodology of polling to server to see if our post has been accepted. Now we check every time our component changes in componentDidUpdate
due to new props being passed, and see if the pendingPost
is in the array of posts
we get from props. If it is, we just reset the form to its initial state so that the user can start posting again.
The Result
With that out of the way, we can fire the project up again with npm run dev
and check out our new and improved Lightning app:
And that’s it for our websocket implementation! You should check out all of the components to see the changes I didn’t cover in detail, and play around a bit yourself to see how much better it works.
Next up for part 4, we’ll be integrating WebLN into our app to allow for a slicker payment experience, as well as message signatures and verification for our posts.