Building a Persistent React Native Chat App: Part I — Database

Technologies: React Native, Redux/React-Redux, Gifted Chat, React Navigation, Socket.io, Sequelize, Postgres

Be sure to check out the two other parts of this series:

Building a persistent, private chat application in React Native from scratch can be confusing. My hope is that this tutorial will help get you up and running in no time. We’ll be creating a straightforward messaging app that you should be able to carry over to any project.

This three-part series will focus first on creating a database for our application that will store users, conversations, and individual messages. After that, we’ll create an HTTP/socket server to handle all our requests. Then, we’ll build our actual React Native app, where I’ll run through using Facebook’s Create React Native App to set up the basic structure for our mobile application, creating the various components we’ll need, and finally rendering the chats themselves.

But that’s enough intro — let’s get coding!

Let’s Build!

We’ll be using Socket.io, Sequelize, Postgres, and an HTTP server on the back end. For the sake of simplicity in this tutorial, I won’t be using Express or any Express routes, but I do recommend incorporating it into your final application, as not everything we build (e.g., logging in users, fetching previous messages) will require Socket.io’s open communication between the server and client.

You might be tempted to try to build your server and React Native application in the same directory. However, this can lead to all sorts of buggy behavior, so it’s best to build them separately.

First, let’s set up a directory for our server, move into it, and initialize NPM and Git:

mkdir chat-app-server
cd chat-app-server
npm init -y
git init

Next, let’s install the various libraries we’ll be using on the back end:

npm install pg sequelize socket.io --save

The file structure for the server will be pretty light:

mkdir db db/models

Creating a Chat Database

Now that that’s set up, we can create our files. Our database doesn’t need to be needlessly complex — a few models and class methods will do. First, let’s create our Postgres database. Assuming you already have Postgres installed, type psql in your terminal to enter the Postgres terminal. Then create your database using create database chat_app_db;. Type \quit to exit the Postgres terminal.

Back in our directory, let’s set up our chat app’s database. I prefer to keep functionality as separate as possible, so we’ll be breaking up our database into a few different files. Use touch db/conn.js to create the connection file. In conn.js, we’ll set up the connection using Sequelize:

If you copy/pasted the create database command above, your database URL will be ‘postgres://localhost/chat_app_db’ . Note that this URL must be a string. Also, PostgreSQL must be running for your server to connect to your database.

Now we can create our database models. We’ll be using three models: one for users, one for messages, and one to keep track of conversations.

I prefer to set up database models in a models directory, just to keep things organized. Use touch db/models/User.js db/models/Conversation.js db/models/Message.js in your terminal to create these files.

Let’s set up our User model:

For the purposes of this tutorial, we won’t be doing true authentication, but handling users in this manner provides a close approximation and will allow us to make sure our chats actually do persist.

Our Conversation model won’t have fields itself. Instead it will function as a join table between two users and have associations with the individual messages in the chat between those users:

Note that we’re destructuringOp — the Sequelize operators — from the Sequelize object, which in turn we’re destructuring from conn(our connection will always include Sequelize, so there’s no need to require it again). We’ll be using the Op.or operator in a class method called Conversation.findOrCreateConversation. We could simply use Sequelize’s findOrCreate method, but since we’ll be using this method in other places and there’s a fair bit of code to the query, we should write our own method in Conversation.js just to keep everything DRY:

Let’s pick apart this code, for clarity’s sake.

Here, we’re returning a Promise chain that first attempts to find a conversation that’s associated with the user IDs we’ve passed in. We’re using eager loading to include all the messages associated with that conversation. Order will matter in our Native app, so we’re using order to sort everything by timestamp. If such a conversation exists, we’ll return it; otherwise we’ll create and return a brand-new conversation. To prevent circular dependency issues, we’re using conn.models.message instead of requiring the Message model directly.

Moving on, let’s set up the Message model:

We’ll be using the React Native Gifted Chat API to render our messages, which will take the message objects from the database and render them. Each message object requires three properties: text, user, and _id . text is simply the actual content of the message. user is an object that represents the sender of the message. It takes an _id and name . (Note: Gifted Chat also allows for the inclusion of an avatar by passing an avatar property to the user object.)

Let’s create a class method that will handle the creation of a message and associate it with the appropriate conversation:

As with the findOrCreateConversation class method, we’re using conn.models.conversation here to avoid any circular dependency issues that might pop up.

With our models set up, we can finally create our associations. I prefer to have one index.js file handle all of our exporting, so we’re going to use touch db/index.js to create this file. For the associations, we’ll need the following:

  • Users will be able to carry on many conversations.
  • Each conversation will need to belong to two users.
  • A message will belong to a single conversation, but a conversation will have many messages.

As such, we can use the following code to create these different associations:

That’s it for our database! In the next part of this series, we’ll create an HTTP/socket server to handle all of our requests.

If you have any questions or comments, feel free to leave them below. Thanks for reading!