Learn How to Build a GraphQL API in Node.js Using Apollo Server
Tags: graphql, api, apollo, node
Published: 2022-01-06 10:26:55

---
title: "Learn How to Build a GraphQL API in Node.js Using Apollo Server"
published: true
description: "Learn the basics of building a GraphQL API in Node.js using Apollo Server"
tags: "GraphQL, API, Apollo, Node"
canonical_url: https://www.awesomecoding.co/posts/learn-to-build-a-graphql-api-in-nodejs-using-apollo-server
---


You might want to build an API to enable external applications like desktop or mobile clients to communicate with your services.

When building a Web API, you can choose from two popular options. These are **REST** and **GraphQL APIs**. Which option you decide to choose depends on various factors. I have [previously written](https://www.awesomecoding.co/posts/building-a-web-api-using-graphql-vs-rest) about the differences between REST and GraphQL APIs. This post will show how to build a GraphQL API in Node.js using **Apollo Server**.

You can find the working code for this post at this Codesandbox:

{% codesandbox apollo-server-example-ojkxu %}

## Apollo Server

Apollo Server is an open-source GraphQL server compatible with any GraphQL client. It is a pretty reliable choice for implementing a GraphQL server on your **Node.js** backend. It is easy to get started and rich with additional features if you want to customize it for your own needs.

## GraphQL Schema

One of the best aspects of working with a GraphQL API is the flexibility that it provides on the client-side. When using a GraphQL API, clients can tailor their own custom queries to submit to the backend. This is a major departure from how the REST APIs work.

This is what a GraphQL query might look like:

```graphql
{
  books {
    title
    author {
      name
      books {
        title
      }
    }
  }
}
```

Here we have a query that is for fetching all the books alongside their title and authors, getting the name of all those authors and all the books that those specific authors have written. This is a deeply nested query, and we could keep nesting it as well!

When we allow the clients to craft their own queries, they are empowered to fetch the exact amount of data they require. A mobile application can be built to query for fewer fields, whereas a desktop application can query for a lot more.

But how does a client know which data to request from the server? This is made possible by something called a **schema.**

GraphQL servers use a definition file called a **schema** to describe the existing types present in the backend so that the client application can know how they can interact with the API.

## Schemas in Apollo Server

One of the major differentiators between GraphQL servers is how they require the schema to be implemented. Apollo Server requires the schema to be implemented using the spec-compliant human-readable **schema definition language** (**SDL**). Here is what SDL looks like:

```graphql
type Book {
  title: String
}

type Author {
  name: String
  books: [Book]
}
```

As you can see, it is fairly easy to understand what types exist and what attributes (or fields) these types have by just looking at this schema written using SDL.

You might have seen other GraphQL server solutions where the schema is implemented by using a more programmatic approach. Here is an example of how schemas are implemented using the `express-graphql` library. (link: https://github.com/graphql/express-graphql)

```javascript
new GraphQLObjectType({
  name: 'Book',
  fields: {
    title: {
      type: GraphQLString,
			// define a resolver here
    },
  },
});
```

These different approaches present a certain kind of tradeoff. SDL makes it easy for anyone to understand what is happening in the schema, while it might be harder to maintain when your schema becomes very large. When schema is programmatic, it might be easier to modularize, customize and scale the schema, but the readability can suffer.

## Getting Started

Let’s create some **mock** **data** to explore building APIs using Apollo Server. For this example, we will be building a GraphQL API for an online store that has a bunch of products and collections that include those products. Our API should be able to fetch and update these products and collections.

We will have two files called **products** and **collections** to contain this data.

**collections.json**

```json
[
  {
    "id": "c-01",
    "title": "Staff Favorites",
    "description": "Our staff favorites",
    "isPublished": true
  },
  {
    "id": "c-02",
    "title": "Best Selling",
    "description": "These are selling out fast!",
    "isPublished": true
  },
  {
    "id": "c-03",
    "title": "In Season",
    "description": "Discover what is in season",
    "isPublished": true
  }
]
```

**products.json**

```json
[
  {
    "id": "random-id-00",
    "category": "apparel",
    "name": "The Best T-Shirt",
    "brand": "A&A",
    "inventory": 32,
    "price": {
      "amount": 100,
      "currency": "USD"
    },
    "collections": ["c-01"]
  },
  {
    "id": "random-id-01",
    "category": "stationery",
    "name": "The Best Pencil Case",
    "brand": "Pencils Forever",
    "inventory": 5,
    "price": {
      "amount": 25,
      "currency": "USD"
    },
    "collections": ["c-02", "c-03"]
  }
]
```

We have three collections and two products. This is enough to get started.

## Setting up Apollo Server

You will need to be comfortable using JavaScript and have a recent version of Node.js (12+) to follow this introduction.

Let’s create a new folder and run `npm init -y` in this folder. This will create a **package.json** file that will keep a record of the project's dependencies. Initially, we will be installing **apollo-server** and **graphql** libraries.

```bash
npm install --save apollo-server@^3.5.0 graphql@^16.2.0
```

We will also install a library called **nodemon** that will automatically restart the server whenever there is a change. This will help us see the results of our updates much faster. This dependency has to do with the development environment, so we will install it using the `--save-dev` flag.

```bash
npm install --save-dev nodemon@2.0
```

We will also create an **index.js** file at the root of this project folder.

```bash
touch index.js
```

We will add a **start** **script** in our **package.json** file to call **nodemon** with our **index.js** file.

```json
"scripts": {
  "start": "nodemon index.js",
  "test": "echo \"Error: no test specified\" && exit 1"
},
```

Let’s create a folder called `data` and place the `collections.json` and `products.json` files into that folder.

We can now start setting up our server in this **index.js** file.

```jsx
const { ApolloServer } = require("apollo-server");

const server = new ApolloServer();

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});
```

We have imported the **ApolloServer** from the **apollo-server** package and trying to run it by calling its `listen` method. We can run this file by calling our **start script.**

```bash
npm start
```

At this point, we would get an error since **ApolloServer** requires you to have **type definitions (schema)** and a **resolver object** on instantiation. We already know what a schema is. A **resolver object** is an object that has a bunch of **resolver functions**. A **Resolver function** is a function that specifies what data should a single GraphQL field return on a query. We don’t have a schema or resolvers, so nothing works.

Let’s start by creating a schema.

## Creating a Schema and GraphQL Types

First, we will import the `gql` function and then create a `typeDefs` variable to pass into the `ApolloServer`.

```jsx
const { ApolloServer, gql } = require("apollo-server");

const typeDefs = gql``;

const server = new ApolloServer({
  typeDefs,
});

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});
```

We can now start declaring types for our GraphQL API inside the backticks for the `gql` function.

Remember the shape of our data for **collections** and **products**. We will start by creating the type definition for a **collection**.

```graphql
type Collection {
  id: ID!
  title: String!
  description: String
  isPublished: Boolean!
}
```

This is a type definition for a **collection** object. Notice how readable it is. Our object has three properties, and we have created a corresponding type with three fields. Note that there **doesn’t** need to be a one-to-one mapping in between a data object and the corresponding type. The GraphQL type represents an interface for a user (client) to interact with. The client might or might not care about the underlying shape of the data. We should make sure to only surface information that the client would care about in a way that is easy to understand.

`Int`, `Float`, `String`, `Boolean`, and `ID` are the most basic types we can use when defining types in GraphQL.

- `Int`: Represents whole numbers.
- `Float`: Represents fractional numbers. (Like `3.14`)
- `String`: Represents textual data.
- `Boolean`: Represents boolean data (Like `true` or `false`)
- `ID`: Represents a unique identifier. GraphQL clients can use this ID for caching / performance optimization purposes. It is recommended that you don’t have this `ID` field be human-readable so that the clients wouldn’t be inclined to implement a logic on their side that relies on a pattern that might surface in the ID. In our example, we will leave the `id` fields to be human-readable, though.

We use `String`, `Boolean`, and `ID` types in our example for collections. Another thing to note is that the usage of the bang symbol (`!`). `!` indicates that the field can **not** be **null** (empty). It has to have value.

Let’s create the type definition for a **product**.

```graphql
type Product {
  id: ID!
  category: String!
  name: String!
  brand: String
  inventory: Int!
  price: Price
  collections: [Collection!]!
}
```

We are using several new types in the **Product** type definition for the following fields:

- **inventory**: `Int` is used for the `inventory` field since the product inventory is defined using whole numbers.
- **collections**: We are defining an array of `Collection` types as the return type of the `collections` field. The `!` usage here suggests that the array can **not** contain a **null** value, and the field can **not** be equal to a null value. So the value can only be an empty array or an array with collection objects inside.
- **price:** Here, we define a new **object type** called `Price` for the `price` field. An object type is a type that includes fields of its own. The definition of that object type will be as follows.
  ```graphql
  type Price {
    amount: Int!
    currency: String!
  }
  ```

There is an enhancement we can make on the **Product** type. Notice how the `category` field is defined as a `String`. The categories in online stores tend to be equivalent to specific values like `apparel`, `accessories`, `stationery`, etc. So instead of defining the `category` \***\*field to be any string, we can define it so that it would only be equivalent to certain values. The way to do that would be using an **enum** type. **Enum** types are useful when defining a set of predefined values for the given field. Let’s create an **enum\*\* type that has three category values.

```graphql
enum Category {
  apparel
  accessories
  stationery
}

type Product {
  id: ID!
  category: Category!
  name: String!
  brand: String
  inventory: Int!
  price: Price
  collections: [Collection!]!
}
```

We are almost done with creating our schema! Finally, we need to define a special object type called **Query** that defines all the top/root-level queries we can run against our GraphQL API.

```graphql
type Query {
  collections: [Collection!]!
  products: [Product!]!
}
```

Here is what the entire schema looks like at this point.

```jsx
const typeDefs = gql`
  type Collection {
    id: ID!
    title: String!
    description: String
    isPublished: Boolean!
  }

  type Price {
    amount: Int!
    currency: String!
  }

  enum Category {
    apparel
    accessories
    stationery
  }

  type Product {
    id: ID!
    category: Category!
    name: String!
    brand: String
    inventory: Int!
    price: Price
    collections: [Collection!]!
  }

  type Query {
    collections: [Collection!]!
    products: [Product!]!
  }
`;
```

We can now pass this schema into our ApolloServer and have things start working!

```jsx
const server = new ApolloServer({
  typeDefs,
});

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});
```

If we are to visit [`http://localhost:4000/`](http://localhost:4000/) or wherever the API is hosted locally, we would land on an Apollo branded welcome page. Let’s click on the big button that reads **Query Your Server.**

![https://i.imgur.com/up3cWDI.png](https://i.imgur.com/up3cWDI.png)

Clicking on that button will take us to a GraphQL explorer interface. Using this interface, we can run GraphQL queries against our API. We can also explore the documentation of our API. Note that we didn’t explicitly write any documentation when building our API. It gets generated automatically using the data already available in the schema. That is a pretty awesome feature of GraphQL! This means that our documentation will always be up to date with our code.

![https://i.imgur.com/8ZIzLHr.png](https://i.imgur.com/8ZIzLHr.png)

Let’s run a query against our GraphQL API. Here is a query that would get the name of all the products

```graphql
{
  products {
    name
  }
}
```

The result would be:

```json
{
  "data": {
    "products": null
  }
}
```

We are getting `null` as a result since we didn’t define any **resolvers** that would specify what this field should return when queried. Under the hood, Apollo Server has created a **default resolver** that is returning a `null` result since this is a **nullable** field.

If we defined the Query object so that the products are **not nullable** then we should ideally receive an empty list as a result.

```graphql
type Query {
  collections: [Collection!]
  products: [Product!]
}
```

However,, Apollo Server default resolver doesn’t take care of that situation, so we receive an error.

## Creating Resolvers

A **resolver** is a function that defines what data a single field should return when queried.

The `Query` type has two fields called `collections` and `products`. Let’s create very simple resolvers for these fields that will return an empty array. We will provide this resolvers object (that contains the resolver functions) inside the ApolloServer function.

```jsx
const resolvers = {
  Query: {
    collections: () => {
      return [];
    },
    products: () => {
      return [];
    },
  },
};

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

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});
```

Now, if we are to run our previous query, we would get an empty array instead. The resolver function we have defined for `products` specifies how that query should be **resolved.**

```graphql
{
  products {
    name
  }
}
```

Let’s create a proper resolver for these fields. We will first import the `collections` and `products` data into `index.js`. Then we will return this data from these queries instead of just returning an empty array. Here is what the implementation looks like.

```jsx
const { ApolloServer, gql } = require("apollo-server");
const collectionsData = require("./data/collections.json");
const productsData = require("./data/products.json");

const typeDefs = gql`
  type Collection {
    id: ID!
    title: String!
    description: String
    isPublished: Boolean!
  }

  type Price {
    amount: Int!
    currency: String!
  }

  enum Category {
    apparel
    accessories
    stationery
  }

  type Product {
    id: ID!
    category: Category!
    name: String!
    brand: String
    inventory: Int!
    price: Price
    collections: [Collection!]!
  }

  type Query {
    collections: [Collection!]
    products: [Product!]
  }
`;

const resolvers = {
  Query: {
    collections: () => {
      return collectionsData;
    },
    products: () => {
      return productsData;
    },
  },
};

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

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});
```

Now that we have defined the resolvers for the `collections` and `products`, we can query these fields for the data they represent. As I have mentioned at the beginning of this article, one of the strengths of GraphQL is the ability for the clients to create their own queries. We can even write a query that would ask for data from these two fields at the same time! This wouldn’t be possible to do in a REST API.

```graphql
{
  collections {
    title
  }
  products {
    category
    name
    brand
    inventory
    price {
      amount
      currency
    }
  }
}
```

We are not including the `collections` field for the `products` in the above GraphQL query. That is because our existing resolver functions currently don’t know how to return the data for that particular field. If we tried to query that field, we would receive an error.

To fix this problem, we need to create another resolver function for the `collections` field of the `Product` type. This resolver function will need to make use of the **resolver arguments**.

```jsx
const resolvers = {
  Query: {
    collections: () => {
      return collectionsData;
    },
    products: () => {
      return productsData;
    },
  },
  Product: {
    collections: (parent, args, context, info) => {
      const { collections } = parent;

      return collections.map((collectionId) => {
        return collectionsData.find((collection) => {
          return collection.id === collectionId;
        });
      });
    },
  },
};
```

### Resolver Arguments

Any resolver function receives four arguments. These arguments are conventionally called `parent`, `args`, `context`, and `info`. Of course, you could choose different names for these arguments depending on your purposes.

For now, we will only take a look at the first two arguments.

**parent**

This argument refers to the return value of the resolver for the field’s parent. In our example, the parent of the field `collections` is a `product`. So this value would be equivalent to a **product** item.

**args**

We could have fields that accepts arguments (a parametrized field). The **args** argument captures the arguments provided by the client to query a parametrized field. We will look into this use case in a bit. For now, we only care about the `parent` argument.

Our resolver function for the `collections` field uses the `parent` argument to fetch the `collections` array of the parent product. We use the `id` data in this array to find and return the collection objects from the `collectionsData`.

```jsx
Product: {
  collections: (parent, args, context, info) => {
    const { collections } = parent;

    return collections.map((collectionId) => {
      return collectionsData.find((collection) => {
        return collection.id === collectionId;
      });
    });
  },
},
```

Now, if we are to run a query that fetches fields of the `collections` field, we would be able to get the collection objects that are associated with each product.

```graphql
{
  collections {
    title
  }
  products {
    category
    name
    brand
    inventory
    price {
      amount
      currency
    }
    collections {
      id
      title
    }
  }
}
```

## Fields with Arguments

As mentioned earlier, we can define fields that would accept arguments in our schema. Let’s create a new field under `Query` type called `productById` that would get the product of a given ID. Here is what that would look like in our schema.

```graphql
type Query {
  collections: [Collection!]
  products: [Product!]
  productById(id: ID!): Product
}
```

`productById` is a field that accepts an `id` argument and returns the product type that has the given id if it exists. Notice the return type for the field doesn’t have the `!` symbol. This means that the returned value can be of type `Product` or `null`. That is because a product of a given id might not exist.

Let’s query this field using the GraphQL API Explorer.

```graphql
query($id: ID!) {
  productById(id: $id) {
    name
  }
}
```

We need to define the parameters that we will pass into this query inside the **variables** section.

```graphql
{
  "id": "random-id-00"
}
```

This is how that screen looks like.

![https://i.imgur.com/6FmV6Y7.png](https://i.imgur.com/6FmV6Y7.png)

We would be getting a `null` as a result of this query since we didn’t implement the resolver function for this field. Let’s do that.

We will be adding a new resolver function under `Query` called `productById`. It is going to fetch the given id from the provided `args` parameter and return the product with the matching id.

```jsx
Query: {
    collections: () => {
      return collectionsData;
    },
    products: () => {
      return productsData;
    },
    productById: (_parent, args, _context, _info) => {
      const { id } = args;

      return productsData.find((product) => {
        return product.id === id;
      });
    },
  },
```

Notice the underscore (`_`) before the argument names that we are not making use of in our function. This is a coding convention to indicate that a named argument to a function is not being used. Now, our previous query should work and return the desired product!

There is a lot more to GraphQL then what I wrote about here but this should be a decent introduction to the subject. In production, we wouldn’t have any hardcoded product or category data in our servers as we did here. We would rather fetch this data from a database or from some other API. When working with data, you might want to use classes called **data sources** that manages how you interact with that data and helps with things like caching, deduplication, etc. You can learn more about data sources [here](https://www.apollographql.com/docs/apollo-server/data/data-sources/).

If you wanted to deploy this API, you can use cloud services such as Heroku, Google Cloud, etc. More information on the deployment process can also be found in the Apollo Server [documentation](https://www.apollographql.com/docs/apollo-server/deployment/heroku/).

You can also see the full code in action at Codesandbox!

{% codesandbox apollo-server-example-ojkxu %}