Using Node.js, GraphQL and React to Build a Real-Time Chat App - Part 2
In part 1 of this tutorial, we used GraphQL server to build the backend of a real-time chat application. In this part, we will be using React, and more specifically React Hooks, to build out the front end of our app. We will use create-react-app to bootstrap and we will connect to our GraphQL server using Apollo Client.
For this app, we want registration for new users to include a username and a valid email address which we will send to our server for creation of a new user account. User details will be retained in the browser's local storage so that sessions can remain active and logged-in users will be able to see who else is logged in so that they can select and chat with them.
Before we get going, please understand that this tutorial is not an introduction to any of the programs that we are going to be using (React, Apollo or ReactHooks). There are many resources available that do an excellent job of teaching you how to use each of these systems. If you are continuing through this tutorial, I will assume that you have some familiarity with the React environment, but if you just have general programming knowledge you should be able to follow these steps.
Another point we need to cover before we start is that you need to have version 16.8 of React or greater installed on your computer to use the new React Hooks. This is important because you will be able to access React Hooks using earlier versions, but it will be the experimental program before it was formally published, so there may be issues or you might not have access to all features. In addition, the published version has code that is cleaner and easy to understand.
The size of the code we will be using is large, so I won't be able to show it all on Andela. I'll try to explain as we move through the process but look out for sections of the code replaced by 's "commented' lines where functions would be placed. Don't worry, you'll be able to obtain the entire code in GitHub, and you can go over there anytime to see the entire gist.
Now that you know what the goal of this tutorial is, let's get started. If you'd like to clone the repo from GitHub and try it out, here is the link:
If you already have create-react-app installed globally on your computer, go to the root folder and run this command:
Take some time to clear the src/folder of all files except index.js, App.js and index.css. While you are still in the src/folder, run the following command:
Next, let's install a few dependencies:
We need to be using Apollo clint on this project because Apollo boost doesn't offer the ability to support subscriptions. Our first step is going to be creating a WebSocket link for subscriptions so that we will be able to deliver the chat messages as well as an HTTP link for any other necessary communication and initialize Apollo Client. This will all be done in the index.js file.
As we have discussed, we are going to be using both HTTP and WebSocket because each will allow for different objects to communicate. Queries and mutations will be communicated through HTTP and subscriptions through ws protocol. The split command will allow each request to move through the correct link. If the request is a subscription split will route it through the wsLink to WebSocket, while all other requests will be directed to the HTTP. We will be using the ApolloProvider component to connect our React app to Apollo Client. This will wrap our React, which allows all clients to be placed in context and allows React to be present everywhere on the app. It is best practice to use the Apollo Provider to wrap your React app above where you are utilizing GraphQL data, which is why we are doing so in the index.js file.
We will be defining our GraphQL query string in the User-Query.js. This includes the query string for all queries, mutations and subscriptions. We are using this to define what is sent to the server as well as what is returned from the server based on the parameters that have been defined by the schema of the server. We are going to wrap each of the subscriptions, queries and mutations with a gql tag and we will also be directly defining the parameters of each. The purpose of the gql function is to take each of the query strings written and supply them to GraphQL AST (Abstract Syntax Tree). We take this step of parsing to GraphQL AST because if successful each of the query strings can be confirmed and a valid response can be sent.
We will be implementing User queries, mutations and subscriptions within an App.js file, so let's move on to that next. We will be using compose and graphql higher order components. You can import each of these from react-apollo so that Apollo is connected to React.
This next section is getting pretty detailed. First, we won't be performing any direct authentication. Instead we will be saving each user that logs in to local storage and retrieves when that user returns to the app. For getting and setting state of both receiverName and receiverMail we will be using the useState hook.
We will be setting up subscriptions in the useEffect hook. The functions of componentDidMount, componentDidUpdate and componentWillUnmount are all compiled into this one hook, which makes it a great place to set up subscriptions. In order to find the subscriptions we want to focus on, we will be using the suscribeToMore tool. The subscribeToMore function allows you to define which subscriptions you would like to listen to, and then implement arguments if there are any specified parameters present on the server. We will be using another function called addUserSubscription to broadcast the arrival of a new user to everyone on the app or announce that a user has left when an account is deleted.
Next, we will determine which functions have moved to graphqlhigher-order components and relay any arguments that may be necessary for the server to execute properly. Our optimistic UI updates will be performed automatically through the Apollo store. Each time a new user is tagged with the createUser function, we let the Apollo store know so that simultaneously the new data is sent to our server and the client profile is also updated. Within the code we have used the graphql higher-order functions wrapper compose. The purpose of this is to inject data to App component as props from Apollo Client. As an example, we receive the createUserMutation tag, we rename it with the alias createUser and then use the new name to call the function while providing the appropriate arguments required for the function to run.
We will be sending users to the User component by using the statement return. This is where we will be rendering users. We also defined setSelectedMail, a function that provides callback, that will be passed as props to the child component. Next, let's move on to the Message element so that we can set up message and userTyping subscriptions.
We will be utilizing React's onChange handler which is used for controlling elements in forms to activate the userTyping subscription. We would like to do even more than changing the state of the element that is input when we define the function. We will be including both the email of the user as well as the user receiving the message. For this function we also want to set a setTimeout feature. If a user were to stop typing for a period of more than 2000ms, the feature will send dummy arguments, and the typing notification prescribed by the subscription will end. Since we don't want multiple setTimeout functions to be engaged at the same time, we also need to include clearTimeout to clear old functions.
Another modification we made to the userTyping mutation is to apply a withFilter () function to the server. This tag specifies that only users with receiverMail on the variable we provide matches the receiverMail on the payload should get the subscription. We included the payload in the handleChange () function. We will be using the useEffect React Hook to pass this variable. In this React Hook, we have assigned a specific variable, receiverMail, as well as given it an email address. The thought process behind this coding is that as I am typing the server is notified of which user I am sending the message to by locating the user's email- receiverMail. I only want the server to return mail from a recipient that has my email address set as their receiverMail, and that person should be the one I am currently chatting with. This is the procedure that makes sure you only receive the typing notification from people you are currently chatting with.
Because every user should have the ability to see messages they sent or received, we are doing a check and displaying messages with the user's email either as senderMail or receiverMail in the return statement.
Well, we have come to a point where the only thing left to include in our app is a front page that will provide a form for users to fill out to log in to the app. We've included a callback that allows for the createUser function in the App to create a new user each time someone submits the form. You can find the details of this callback feature by referencing the repo on GitHub.
You did it! You have created a fully functional, working, real time chat application. I hope you found this tutorial helpful, and had fun along the way. I certainly did! To prevent this post from becoming too tedious, I'll sign off here.
Go ahead and run npm start, check out localhost:3000 and enjoy!
There are so many other fun features I would love to include, namely push notifications. Maybe take some time to play around and figure out what else you would like to include in your app. I absolutely think a chat app should have push notifications though, don't you? Maybe another lesson for another day. You may think about including an authentication feature that allows users to login with just their username and email. Maybe even think about including login access from social media accounts like facebook, twitter, google or Github. There are so many fun options's —
Thank you so much for taking the time to go through this journey with me.
Did you find this Andela blog useful? You can view a short demo of Chukwualuka's chat app here.
Are you a developer interested in growing your software engineering career? Apply to join the Andela Talent Network today.
Your career is a journey, not just a job. Taking ownership of your career development and actively seeking out opportunities for advancement can not only spark career growth, but also increase your enthusiasm for your work. Read our seven tips to accelerating your work ambitions!
With technology advancing faster than ever before, tech skills are always in demand. These are the top six right now: Core engineering, Cloud API, database expertise, data analytics, communications, and Devops methodology.