Introduction
In a previous post titled GraphQL: An Introduction, 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
- Create a new directory for your project and navigate to it using your terminal.
- Initialize an npm project by running
npm init -y. - Install the required dependencies by running the command
npm install express express-graphql graphql graphql-tools. - Install the dev dependencies by running
npm install -D typescript ts-node @types/node @types/express @types/graphql nodemon.
Configuring Typescript
- create a
tsconfig.jsonfile 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
- Create a directory in the root directory of your project and name it
src. - Create five files in the
srcdirectory 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.tsfile. -
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.
- Open the
server.ts fileand setup the express server as below
import express from "express";
const server = express();
export default server;
- 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}`);
});
- 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.
Defining the GraphQL schema
- Open the
schema.tsfile 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
idfield of typeID, anamefield of typeString, anagefield of typeInt, apictureUrifield of typeString, and anownerNamefield of typeString. - Next, we created two queries. A
getPetsquery which returns an array ofPetObjects and agetPetquery which takes anidas an argument and returns aPetObject. - We also created a
createPetmutation which returns the created pet object, aupdatePetmutation which returns the updated pet object and adeletePetmutation 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
- Open the
resolvers.tsfile 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 thegetPetquery and it takes anidargument of typestringand 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 thegetPetsquery. -
createPet: this is responsible for creating a pet and requires theid,name,pictureUri,ownerNameandagewhich are allstringtypes except for theageargument which is a number type. -
updatePet: this is responsible for updating a pet and take a requiredidargument of typestringas well as other optional arguments i.e.name,pictureUri,ownerNameandage -
deletePet: this takes a requiredidargument of typestring, 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.
- 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


Top comments (3)
express-graphql package is depreciated
That post was about a year ago. I'll probably write a new article using the graphql-http package.
Thanks for this post. Some pointers:
.graphqlfile.