Create a GraphQL application with Node.js and Apollo server 4

Photo by Wes Hicks / Unsplash
Photo by Wes Hicks / Unsplash

Photo by Wes Hicks / Unsplash

GraphQL is a query language for APIs that makes it easier to extract data from a  data source. With the high adoption of Software oriented architecture, here are some use cases where you could need GraphQL:

  • You have various clients that need different data.
  • You are fetching more than you need or less than you need.
  • You make multiple calls to fetch data that are linked between them.
  • You want auto-generated documentation from the GraphQL schema.
  • Built-in type checking and validation of the input.
  • Supported by the most popular programming languages.
  • A large community-building tool around to make the usage easier.

GraphQL is always compared to REST API, another way of building API. If you are interested in building one using Node.js, I wrote a post about it.

Create a REST API with Node.js, Express, MongoDB and Typescript
In this post, we will build a REST API for a user management application in Node.js and Typescript. Connect to a MongoDB database and store the data.

What we will build

Let's say you are building a note-taking app where each user will sign up before starting to take notes. The backend system will expose a GraphQL API that allows user registration and also retrieve the list of users registered.

We will see how to set up a GraphQL server in Node.js using Apollo server 4, which provides great features compared to the previous version.

Prerequisites

To follow this post, you will need the following tools:

Set up the project

To start, we will use a boilerplate for the Node.js project we built on this tutorial.


git clone https://github.com/tericcabrel/node-ts-starter.git node-graphql

cd node-graphql

yarn install

yarn start

Now the project work as expected, let's install the libraries required to create a GraphQL server:


yarn add @apollo/server graphql

Define the GraphQL schema

A GraphQL schema contains the definition of types and operations describing how clients can interact with the API. This schema will act as a contract between the server implementation and the client that consumes the API.

Let's create a file called schema.graphql and add the code below:


type User {
    id: ID!
    firstName: String!
    lastName: String!
    email: String!
    password: String!
}

input CreateUserInput {
    firstName: String!
    lastName: String!
    email: String!
    password: String!
}

type Mutation {
    registerUser(input: CreateUserInput!): User!
}

type Query {
    users: [User!]!
}

Here, we define the schema for the User, then, create a mutation registerUser() to register a user, and finally define a query listUsers() to retrieve users.

Before writing the resolvers for the mutation and the query, we will first generate the definition of the types from the GraphQL schema to enforce the type check while writing the mutation and query implementation.

Generate type definitions

GraphQL code generator is a CLI tool that generates types definitions from the GraphQL schema. It avoids us writing the same types which are already described by the GraphQL schema.

So, once we change the schema, we will run a command to update the definition of the auto-generated types. For that, we need three Node packages:

  • graphql-codegen/cli: command-line tool for the code generator.
  • graphql-codegen/typescript: generate types definitions from the schema.
  • graphql-codegen/typescript-resolvers: generate the method signature for a resolver.

yarn add -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

We only need them during our development; this is why they are installed as development dependencies.

We must define a configuration that will be used by codegen to know where the GraphQL schema is located and also the path to store the generated types. Let's create a file named codegen.yml at the project root directory, then add the code below:


overwrite: true
schema: src/schema.graphql
documents: null
generates:
  src/types/types.d.ts:
    plugins:
      - "typescript"
      - "typescript-resolvers"
      

The property schema takes the location of our GraphQL schema, and we want the output to be stored in src/types/types.d.ts.

Run the command below to generate the types:


yarn graphql-codegen

Check the file generated but don't edit anything inside, and if it happens and you don't know how to go back, just retype the code above to regenerate.

Handle custom data type in a GraphQL application with Node.js
In this tutorial, we will see how to create a custom Date type, apply validation on the value with GraphQL and use it in our GraphQL schema and resolvers.

Create the data source

Apollo can fetch data from various data sources like databases, REST or GraphQL API, RPC, and static data. To stay focused on GraphQL things, we will create a file that will act as our data source with some existing users by default.

Create a file src/datasource.ts and add the code below:


export default [
  {
    email: 'tlannister@got.com',
    firstName: 'Tywin',
    id: '1',
    lastName: 'Lannister',
    password: 'my-very-secured-password',
  },
  {
    email: 'lmormont@got.com',
    firstName: 'Lyanna',
    id: '2',
    lastName: 'Mormont',
    password: 'my-very-secured-password',
  },
];

Create resolvers for the mutation and query

Create a file src/resolvers.ts and add the code below:


import { CreateUserInput, MutationResolvers, QueryResolvers, Resolvers } from './types/types';
import datasource from './datasource';

const createUser: MutationResolvers['registerUser'] = (parent, args) => {
  const { email, firstName, lastName, password }: CreateUserInput = args.input;

  const user = {
    email,
    firstName,
    id: `${datasource.length + 1}`,
    lastName,
    password,
  };

  datasource.push(user);

  return user;
};

const findAllUsers: QueryResolvers['users'] = () => {
  return datasource;
};

const resolvers: Resolvers = {
  Mutation: {
    registerUser: createUser,
  },
  Query: {
    users: findAllUsers,
  },
};

export { resolvers };

Create GraphQL server with Apollo server 4

Replace the content of the index.ts with the code below:


import { readFileSync } from 'fs';
import { resolve } from 'path';
import { startStandaloneServer } from '@apollo/server/standalone';
import { ApolloServer } from '@apollo/server';
import { resolvers } from './resolvers';

type ApolloContext = {};

const GRAPHQL_SCHEMA_PATH = resolve(__dirname, 'schema.graphql');

const typeDefs = readFileSync(GRAPHQL_SCHEMA_PATH, { encoding: 'utf-8' });

const server = new ApolloServer<ApolloContext>({
  typeDefs,
  resolvers,
});

startStandaloneServer(server, {
  listen: { port: 4000 },
}).then((result) => {
  console.log(`🚀 Server ready at: ${result.url}`);
});

Start the server by running the command yarn start, then open your browser and navigate to http://locahost:4000. You get the page below:

Apollo studio explorer home page.
Apollo studio explorer home page.

Click on the button Query your server; you will be redirected to the Apollo sandbox, which is an editor where you can execute your graphQL queries:

Successfully register a new user using the mutation registerUser().
Successfully register a new user using the mutation registerUser().

The Sandbox UI includes:

  • An Operations panel for writing and executing queries (in the middle)
  • A Response panel for viewing query results (on the right)
  • Tabs for schema exploration, search, and settings (on the left)
  • A URL bar for connecting to other GraphQL servers (in the upper left)

Note: if the environment variable NODE_ENV is set to productionIntrospection is disabled by default. You will need to enable it manually so that Apollo sandbox can query the schema definition.

To do that, update the Apollo Server instance creation to this:


const server = new ApolloServer({ 
  typeDefs,
  resolvers,
  introspection: true
});

Wrap up

We reached the end of this tutorial, and we saw how to set up a GraphQL server with Apollo server 4. To go further, check out the Apollo documentation.

Find the final source code on the GitHub repository.

Follow me on Twitter or subscribe to my newsletter to avoid missing the upcoming posts and the tips and tricks I occasionally share.