Part 2 from series: How to build a real-time chat app

Create the GraphQL API

Setup GraphQL with Apollo Server

We will setup Apollo Server that will manage communication for our GraphQL service. This will also be configured with an Express server as middleware that will allow us to add more endpoints such as a healthcheck endpoint.

In your terminal or command prompt, change to your project directory. If you're already in the wep-app directory, use the following:

cd ../

Run the following command to clone a boilerplate project. This repo has minimal setup needed for this tutorial from github:

git clone https://github.com/saedwards/messaging-web-api-boilerplate

A folder called web-api will be created in your project folder. The project sets up some key dependencies such as Apollo Server, Express and GraphQL aswell as the Slack client to communicate with the messaging service.

Next, run npm install to install project dependencies.

It's worth taking some time to look at the index.js file created. Inside there lives the configuration for our server. It includes the GraphQL Schema where we will define the shapes of data that our GraphQL endpoint accepts. It also sets up the Apollo Server, Express middleware with registration of our GraphQL Schema to the Apollo Server.

A short introduction to GraphQL schemas

Defining queries within our GraphQL schema

Next we will define a query for retrieving a list of messages in the GraphQL schema. From your project folder, open the file web-api/index.js in your favourite IDE.

Below our import statements, we will change our type definitions to define a query that will describe retrieving a list of all messages.

Change from:

web-api/index.js
const typeDefs = gql`
  type Query {
    test: string
  }
}`;

Change to:

web-api/index.js
const typeDefs = gql`
  type Query {
    messages(max: Int): [Message]
  }
}`;

Here we've defined a query that will resolve to a list of Message objects. This is denoted by [Message] surrounded by square brackets to indicate a collection returned. We also specify a variable for the query called max, which is typed as an integer. As you might have guessed, we will use this variable to limit the amount of messages returned in the query.

Next we need to define our Message model. Create the following folder structure and file within the web-api directory: api/messages/message.model.js. Within this file add the following code.

web-api/api/messages/message.model.js
const { gql } = require('apollo-server-express');

const messageGQL = gql`
  type Message {
    id: ID
    userId: String
    text: String
    timestamp: String
    fromYou: Boolean
    #user: User
  }
`;

module.exports = { messageGQL };

This will be the shape of our Message object returned from our GraphQL API. It includes a userId, which will be used to fetch the user before the model is constructed, a text property to store the actual message that was sent, a timestamp to know when the message was sent, which will help in ordering messages. It includes a fromYou flag to indicate whether the message was sent from the anonymous user, which will help to customise the UI for the person using our app. And a user property to store details of the user associated with the message.

Note the user property of our Message model is commented out for now. Back in our index.js file, we need to import the new file and add the Message model to our GraphQL Schema.

web-api/index.js
//Existing code...

const { messageGQL } = require('./api/messages/message.model');

const typeDefs = gql`
  #...

  ${messageGQL}
`;

We need to define a user model for each message so we can associate a message to an individual.

Create the following folder structure and file within the web-api directory: api/messages/user.model.js

Within this file add the following code.

web-api/api/messages/user.model.js
const { gql } = require('apollo-server-express');

const userGQL = gql`
  type User {
    id: ID
    name: String
    colour: String
    avatarUrl: String
  }
`;

module.exports = { userGQL };

The User object will store information about the user that we'll use to format individual messages. Again, we need to add the User model to our main schema by using the userGQL export:

web-api/index.js
//Existing code...

const { messageGQL } = require('./api/messages/message.model');
const { userGQL } = require('./api/users/user.model');

const typeDefs = gql`
  #...

  ${messageGQL}
  ${userGQL}
`;

Our Message model can now reference our new User type in file api/messages/message.model.js by removing the line comment # before the user property (to leave user: User):

web-api/api/messages/message.model.js
const messageGQL = gql`
    type Message {
        #...
        user: User
    }
`;

These are all the GraphQL models that we will need to build our messaging application.

Apollo Server gives us some useful tools to test requests against our schema and view responses. From the web-api root in your terminal or console, run node index.js to start the GraphQL server. Open the GraphQL playground at http://localhost:4123/graphql. Click the SCHEMA tab on the right-hand side. In here you'll see all the model types that we've defined. These are the shapes of our requests and responses. Click the top-level messages query to view what a type of Message looks like. Most properties are primitives such as strings or booleans. Click the user property to see our custom User type that is to be associated with each message.

This tool is very useful for developing and understanding GraphQL schemas without the need of running your application to test data requests/responses.

Collapse the SCHEMA tab. You'll see a play button with two panels either side. The left panel is where we can structure our requests, the right panel is where we view the server response after we send our request.

Copy and paste the following code into the left panel:

query Messages {
  messages(max: $max) {
    userId
    text,
    timestamp
    user {
      id
      name
      colour,
      avatarUrl
    }
  }
}

This is an example of a GraphQL query that we can send from our application as a string in a request body to retrieve a list of messages. The properties specified correspond to the data we would expect to receive in each message of the response. To build the response, GraphQL will need to make a request to a data source for message data and in separate requests fetch the user for each message.

The great thing about this is, if the application doesn't need properties such as the user when displaying a message and doesn't include this in the request, the GraphQL API will not do anything to fetch any user data associated with each message even though we called the same endpoint. This will increase user-perceived performance and response-times, whilest also reducing server load.

Below the request panel under 'Query Variables' insert the following code:

{
  "max": 10
}

Variables can be used to populate different values for the same query. We are going to restrict the amount of messages we send back in the response by specifying a max variable of 10, which we will use to limit the response to sending 10 messages of data.