How to Build a GraphQL API with Express and TypeScript
Tags: graphql, typescript, express, api
Published: 2023-07-16 15:10:50

## Introduction

In a previous post titled [GraphQL: An Introduction](https://dev.to/alahirajeffrey/graphql-an-introduction-2plm), we delved into what GraphQL was and touched on how the GraphQL architecture differed from the REST (Representational State Transfer) architecture, various GraphQL concepts as well as situations where it was appropriate to use GraphQL as opposed to REST. 

In this post, we are going to learn how to build GraphQL APIs with express and typescript by building a simple barebones API that allows users to share information about their pets.

## Prerequisites

Before we begin, make sure you have Node.js and npm (Node Package Manager) installed locally. Additionally, basic knowledge of JavaScript, Node.js, and TypeScript would be beneficial.

## Initializing the Project

1. Create a new directory for your project and navigate to it using your terminal.
2. Initialize an npm project by running `npm init -y`.
3. Install the required dependencies by running the command `npm install express express-graphql graphql graphql-tools`.
4. Install the dev dependencies by running `npm install -D typescript ts-node @types/node @types/express @types/graphql nodemon`.

## Configuring Typescript

1. create a `tsconfig.json` file in the root directory of your project and save the configuration below.

```
{
  "compilerOptions": {
    "target": "ES2019",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "outDir": "./dist"
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}
``` 

## Setting up the Project

1. Create a directory in the root directory of your project and name it `src`.
2. Create five files in the `src` directory and name them: 

- `server.ts`: this file would hold and export the code for our Express GraphQL server.
- `index.ts`: this file would be responsible for importing and running the Express GraphQL server which would be imported from the `./server.ts` file. 
- `database.ts`: this file would hold our mock database as we would not be using a real database for the sake of simplicity.
- `resolvers.ts`: this file would hold the resolvers. Resolvers are functions that are responsible for generating responses for GraphQL queries.
- `schema.ts`: This file would hold our GraphQL schema including all the available types, mutations, and queries.


3. Open the `server.ts file` and setup the express server as below

```
import express from "express";

const server = express();

export default server;
```

4. Update the index.ts file with the following code

```
import server from './server';

const PORT = 3000;

server.listen(PORT, () => {
  console.log(`Server is running on localhost:${PORT}`);
});
```

5. Open up the package.json which holds all the project's metadata and is located at the root of your project directory and update the scripts section as seen below

```
"scripts": {
  "start": "node dist/index.js",
  "start:dev": "nodemon --watch src --exec ts-node src/index.ts"
}
```

Upon completion of the steps above, running the command `npm run start:dev` would start the project using nodemon which would automatically restart the project once changes are made and saved. You should see something like the image below.


![nodemon setup](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5b2eqnh8m545rq0nh1v.png)

## Defining the GraphQL schema

1. Open the `schema.ts` file you created earlier and type the code below

```
import { buildSchema } from "graphql";

const schema = buildSchema(`
   type Pet {
    id: ID!
    name: String!
    age: Int!
    pictureUri: String
    ownerName: String!
  }

  type Query {
        getPets: [Pet]
        getPet(id: ID!): Pet
    }

  type Mutation {
        createPet(name: String!, age: Int!, pictureUri: String, ownerName: String!): Pet!
        updatePet(id: ID!, name: String, age: Int, pictureUri: String, ownerName: String): Pet!
        deletePet(id: ID!): ID!
    }
`);

export default schema;
```

Let's break this code down a little bit:

- The schema variable in the code is responsible for holding our GraphQL schema.
- We created a pet schema that has 5 fields. An `id` field of type `ID`, a `name` field of type `String`, an `age` field of type `Int`, a `pictureUri` field of type `String`, and an `ownerName` field of type `String`.
- Next, we created two queries. A `getPets` query which returns an array of `Pet` Objects and a `getPet` query which takes an `id` as an argument and returns a `Pet` Object.
- We also created a `createPet` mutation which returns the created pet object, a `updatePet` mutation which returns the updated pet object and a `deletePet` mutation which returns the id of the deleted pet.

It is important to note that the ! symbol which is used in several fields in the GraphQL schema implies that the field is required.

## Creating the resolvers

1. Open the `resolvers.ts` file and write the following code

```
import pets from "./database";
import { randomUUID } from "crypto";

type Pet = {
  id: string;
  name: string;
  age: number;
  pictureUri: string;
  ownerName: string;
};

const getPet = (args: { id: string }): Pet | undefined => {
  return pets.find((pet) => pet.id === args.id);
};

const getPets = (): Pet[] => {
  return pets;
};

const createPet = (args: {
  name: string;
  age: number;
  pictureUri: string;
  ownerName: string;
}): Pet => {
  // generate randon uuid for pet object
  const generatedId = randomUUID().toString();
  // create pet object and save
  const pet = { id: generatedId, ...args };
  pets.push(pet);
  return pet;
};

const updatePet = (args: {
  id: string;
  name?: string;
  age?: number;
  pictureUri?: string;
  ownerName?: string;
}): Pet => {
  // loop through pets array and get object of pet
  const index = pets.findIndex((pet) => pet.id === args.id);
  const pet = pets[index];

  // update field if it is passed as an argument
  if (args.age) pet.age = args.age;
  if (args.name) pet.name = args.name;
  if (args.pictureUri) pet.pictureUri = args.pictureUri;

  return pet;
};

const deletePet = (args: { id: string }): string => {
  // loop through pets array and delete pet with id
  const index = pets.findIndex((pet) => pet.id === args.id);
  if (index !== -1) {
    pets.splice(index, 1);
  }

  return args.id;
};

export const root = {
  getPet,
  getPets,
  createPet,
  updatePet,
  deletePet,
};

```

First and foremost, we created a Pet type to enable type checking in typescript. After that, we created five resolvers for the queries and mutations:

- `getPet`: this resolver returns a response for the `getPet` query and it takes an `id` argument of type `string `and returns a pet object if it exists.
- `getPets`: this resolver gets all the pet objects that are currently in the pets array. It is responsible for returning a response for the `getPets` query.
- `createPet`: this is responsible for creating a pet and requires the `id`, `name`,`pictureUri`, `ownerName` and `age` which are all `string` types except for the `age` argument which is a number type.
- `updatePet`: this is responsible for updating a pet and take a required `id` argument of type `string` as well as other optional arguments i.e. `name`, `pictureUri`, `ownerName` and `age` 
- `deletePet`: this takes a required `id` argument of type `string`, deletes the pet object, and returns the pet id.

## Update Code

After writing the schemas and resolvers, the next step is to update the server to make use of the schema as resolvers. Ensure your `server.ts` file looks like the one below

```
import express from "express";
import { graphqlHTTP } from "express-graphql";
import schema from "./schema";
import { root } from "./resolvers";

const server = express();

// setup graphql
server.use(
  "/graphql",
  graphqlHTTP({
    schema: schema,
    rootValue: root,
    graphiql: true,
  })
);

export default server;

``` 

## Testing the API 

If you have reached this stage without problems, congratulations. Now all that's left is for us to test the API. We do this by running the server. Simply type the command `npm run start:dev` into your terminal and navigate to `http://localhost:3000/graphql` in your browser. You should see the GraphQL playground as shown below.


![graphiql](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2tjngdrfsaqr4h51nki0.png)  

- Create a pet by typing the code below into the playground

```
mutation{createPet(name:"lubindos",age: 10, ownerName:"jeffrey",pictureUri:"pictureUri"){
  name,
  ownerName,
  pictureUri,
  age,
  id
}}
```

- Update a pet 

```
mutation{updatePet(id:"081d0b95-8421-4cb0-8089-c5b8642731ae",name:"lubindos junior",pictureUri:"pictureUri"){
  name,
  ownerName,
  pictureUri,
  age, 
  id
}}

```

- Delete a pet

```
mutation{deletePet(id:"cf31ec68-ada2-46c0-b4a2-9dedd14d2176")}

```

- Get a pet

```
{
  getPet(id:"52599910-16fa-4744-a3fa-7900a8b70185"){
    id,
    age,
    name,
    ownerName,
    pictureUri
  }
}
```

- Get all pets

```
{
  getPets{
    id,
    age,
    name,
    ownerName,
    pictureUri
  }
}
```

**NB**: Dont forget to edit the fields in the mutation

## Conclusion

In this tutorial, we dived a little bit deeper into how to create a GraphQL API with express and typescript by building a simple API. In the next tutorial, we would use the nestjs framework as well as Postgres database and an ORM (Object Relational Mapper) to learn real-world use cases.

Link to code on github: [pets-graphql-api](https://github.com/alahirajeffrey/pets-api)