How to build a real-time chat application with Node.js, GraphQL and React — 2

Chukwualuka Chiama
The Andela Way
Published in
7 min readJul 23, 2019
Image by Peggy und Marco Lachmann-Anke from Pixabay

What are we building?

App demo

Introduction

In part 1 of this tutorial, we succeeded in building a GraphQL server for a real-time chat application. Now, we’re going to build the frontend with React- and we’ll be using React Hooks. We’ll bootstrap this using create-react-app and we’ll use Apollo Client to connect our GraphQL server to React.

We want our users to be able to register with an email and username. If they enter valid details, we’ll send the data over to our server to create a new user. Then we will save the user’s details in the browser’s local storage so that we can persist the session. Any logged-in user will see other logged-in users and can select any of them to chat with.

Please note that this is not an introduction to React, Apollo or React Hooks. There are many other resources that do those well. I will assume you’re already a bit familiar with the React ecosystem. If you aren’t but have some programming experience, then you will be able to follow.

React Hooks were introduced in React 16.8. Ensure you have this version of React before attempting to use Hooks except you’re going to use the experimental version that was introduced before React 16.8. If you have used React before, you will love Hooks. They make code cleaner and easier to understand.

We won’t be able to show all our code here because of its sheer size. I’ll try to explain as we move along. Look out for ‘commented’ lines in GitHub gists that say what functions would be in those places. Don’t worry about those because you can get all of the code on GitHub. Skip over there to see the gist in full.

Now that you have a picture of what we want to achieve, let’s get to work. You can clone the repo from GitHub and take it out for a spin:

Install Dependencies

Navigate to your root folder and on your terminal, run this if you have create-react-app installed globally:

create-react-app client

Clear the src/ folder of all unwanted files. Leave only the index.js, App.js and index.css files. Run this command on the terminal while in the src/ folder:

$ rm app.test.js logo.svg app.css && touch User.js Message.js Frontpage.js Message-Query.js User-Query.js

Let’s install some dependencies:

npm i graphql react-apollo apollo-link-ws subscriptions-transport-ws apollo-cache-inmemory

If we weren’t implementing subscriptions, Apollo-boost might have been enough. For subscriptions, however, we need to implement some low-level functionalities that Apollo-boost doesn’t offer out of the box so we’ll use Apollo Client instead. We’re going to start by creating a WebSocket link for the subscriptions we’ll use to deliver our chat messages, an HTTP link for other communication with the server and initializing Apollo Client. You’ll be doing this in your index.js file like so:

index.js

We have both an HTTP link and a WebSocket link. Queries and mutations go over HTTP while subscriptions are done over the ws protocol. splitroutes each request to the appropriate link that will handle it. If it is a subscription, split sends the request through the wsLink route which if an instance of the WebSocket link. Otherwise, the request is sent through HTTP. To connect Apollo Client to our React app, we use the Apollo Provider component. This will wrap our React component, place the client on the context and provide it everywhere on the app. It’s always best to wrap your React app with the Apollo Provider high above anywhere you will need GraphQL data. That is why we are doing this in the index.js file.

App.js

User-Query.js, shown above, is where we define our GraphQL query string for all user queries, mutations, and subscriptions. Everything not shown follows the pattern of the UserQuery. We’re defining what we send to the server and what we get in return. These parameters were defined in the schema in the server. We’re wrapping our subscriptions, queries, and mutations, with the gql tag and defining the parameters each takes. The gql function takes the query strings we’ve written and parses them to the GraphQL AST(Abstract Syntax Tree). If the query strings we wrote are successfully parsed to the GraphQL AST, then they can be validated and a response sent.

Let’s move on to our App.js file where we will implement User queries, mutations and subscriptions here. We‘re going to use compose and the graphql higher-order components imported from react-apollo to connect Apollo to our React application.

App.js

A lot of things are happening here. First, because we aren’t doing authentication we’ll just save the user logged in to local storage and pull it from there whenever the user returns. Then we’re using the useState hook for getting and setting state for both receiverName and receiverMail.

Subscriptions are set up in the useEffect hook. This hook provides you the functions of componentDidMount, componentDidUpdate and componentWillUnmount all combined. It’s the ideal place to set up subscriptions. To define the subscriptions we want to listen to, we are going to use the subscibeToMorefunction. With the subscribeToMore, you can define the subscription you want to listen in on and then pass arguments if you’ve specified any parameters on the server. For the addUserSubscription, we want to get new users and broadcast them immediately to everybody. When a user is deleted, we also want to broadcast that user to everybody.

Next, we define the functions passed to our graphql higher-order component and pass the arguments needed for the server to execute them. We’re going to be doing optimistic UI updates courtesy of the Apollo store. When a new user is created with the createUser function, we update the Apollo store so that our data is not only sent to the server but we see the mutation take effect on the client immediately. Notice that with the graphql higher-order functions wrapper with compose, we’re injecting data to the App component as props from Apollo Client. For example, the createUserMutation is provided to us as props, we gave it an alias as createUser and then we called the function and provided arguments defined on the server for the function to run.

In the return statement, we’re sending users to the User component. We will render users there. We’ve also defined a callback function, setSelectedMail, that we’re passing as props to the child component. Let’s hop over to the Message component to see how to set up message and userTyping subscriptions.

Message.js

To implement the userTyping subscription, we have the advantage of React’s onChange handler function for controlled elements in forms. When we define the function, we want to do more than just change the state of the input element. We pass in the email of the user typing as well as the email of the user at the receiving end. We also want a setTimeout that will fire after 2000ms, sending dummy arguments to the function, so that if the user stops typing, the typing notification will end. To ensure that multiple setTimeout functions don’t get stacked, we clear them with clearTimeout.

In the server, we used the withFilter() function to specify that users should only get subscriptions when the receiverMail on the variable we provide is same as the receiverMail on the payload from the userTyping mutation. We just passed the payload in the handleChange() function. We’re going to pass this variable in the useEffect Hook. You’ll notice that in this Hook, we’ve defined the variable, receiverMail, and assigned an email to it. The logic is this: when I’m typing I notify the server of the user I’m sending a message to by specifying the user’s email- receiverMail. I want the server to return my email, but only if this user has my email as their receiverMail, and that will only be a user chatting with me. And with that, you get notified only when the user you’re chatting with is typing.

In the return statement, we’re simply doing a check and displaying messages that have the user’s email either as senderMail or as receiverMail. Every user gets messages she has sent or received.

Aside from the component that renders our users, which I mentioned above, the only thing left in our app is a front-page with a form for users to use for logging in. We’ve passed a callback from the App component so that when a user submits this form, the createUser function in the App component is implemented and a new user is created. Quickly check the repo on GitHub to see the details.

There you go. You have a fully functional real time chat application working. I hope you enjoyed this and found it interesting. I sure did. To prevent this from becoming too long and boring, I’ll have to stop here.

Run npm start, head over to localhost:3000 and have some fun.

There are lots of other implementations I would love to add that you can implement on your own to learn some more. Top on the list for me would be push notifications. A chat app should have push notifications, shouldn’t it? Yeah yeah… That will be a lesson for another day though. You could add proper authentication so that users sign up and then log in with their emails and passwords. You could also integrate social media login so users can log in with Facebook, Twitter, Google or even GitHub. There’s so much fun stuff to do…

Well, thank you very much, good reader, for taking out time to read this. Comments and suggestions would be appreciated. I’d love to hear other opinions and learn some more. Questions will also be welcome. Use the comment box as you wish.

--

--

Chukwualuka Chiama
The Andela Way

Learner, Grower, Software Developer. I believe in the beauty of our individual songs.