Complete Guide To Build a CRUD API With Node.js and GraphQL

Building a CRUD API lets one know how the communication between the frontend and the backend of the applications works. Using Node.js and GraphQL, you can build a CRUD API that you can connect to virtually any database. 

Here are the steps to build a CRUD API with Node.js and GraphQL:

  1. Set up a new npm package
  2. Install Express and GraphQL
  3. Create an index.js file 
  4. Create a schema.graphql file to define schema
  5. Define resolvers in the index.js file
  6. Test the CRUD API 

Read on to learn how to build a CRUD API using Node.js and GraphQL!

What is CRUD?

CRUD is an acronym for Create, Read, Update and Delete. CRUD APIs provide the basic functionality to manipulate the data in four ways. CRUD is considered a basic cycle. In any given database, it is almost always necessary to have a means of creating or adding new data, reading the given data stored, updating the data already present, and deleting data that is old or not needed.

Steps to build a CRUD API with Node.js and GraphQL

Set up a new npm package

Before building your API, you will need to create a Node.js project, as, without that, it will not be possible to build your API.

Run the following command to build a new npm package:

npm init

After running the command, keep pressing enter until the command has finished running. This will set everything to default.

The above command creates a new node package for you. In that package, there is a file known as package.json. Without this file, your node.js project can’t run. The package.json file contains all dependencies and version information etc.

Install Express and GraphQL

To build a CRUD API, you will need to install Express and GraphQL so you can access the necessary libraries required to build the API. 

  • Express is a Node.js web application framework that backend developers use as it provides multiple features which assist developers in developing mobile and web applications. Simply put, it is a layer on top of the existing Node.js runtime environment which helps manage routes and servers.
  • GraphQL is a query language used to communicate with databases of different kinds. GraphQL allows you to build APIs which can be evolved and provide the client with access to multiple resources without having to type multiple queries. 

You can use the following command to install Express and GraphQL:

npm install express  express-graphql  graphql  graphql-tag

Creating a Dummy Database (Optional)

For the sake of simplicity, in this article, you will be creating a dummy database using JSON objects. This dummy database will act as an actual database, such as the ones in SQL or MongoDB. The reason for not choosing an actual one is to remove the setup complexity and keep focusing on creating an API.

To create a dummy database, go through the following steps:

Run the following command:

npm install notarealdb

Create a new folder in your c.urrent directory, and then inside this folder, create a new json file name database.json. Now, create fake data using JSON objects. An example is written below:

{
    "id": "1234",
    "name": "James",
    "age": 34
  },...

The data provided above is in a JSON object format. Therefore, all your data will be inside an array of JSON Objects, each array member being of type Student. GraphQL itself maps the JSON objects to the schema you provided. Below is the complete data set we will be using for this guide:

[
{
"id": "1234",
"name": "James",
"age": 34
},
{
"id": "1235",
"name": "Susan",
"age" : 28

},
{
"id": "1236",
"name": "Alex",
"age": 27
}
]

Create an index.js file

You will need to create an index.js file which will act as your main file in which you will write your queries and resolve all the resolvers present in the schema. 

Adding Relevant Libraries

Inside your index.js file, you will need to add libraries. These libraries will allow you to take advantage of the functions to build your API.

Write the following into your index.js file:

//importing relevant libraries

const express = require('express');              
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const fs = require("fs");
const { DataStore } = require('notarealdb');
const { report } = require('process');

One thing to note here is how the libraries are imported. The syntax:

const{ <<function name>> } = require ('<<library name>>');

The above is used to just import a particular function or a class from the library instead of the entire library.

Creating GraphQL webpage for query testing

Create an index.js file inside your node package. Inside this file, for now, add the following block of code:

//Creating a GraphQL API Server

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');

The above piece of code allows you to create a GraphQL API server. Through this URL, you can test your queries and mutations and will be able to see the data they are returning.

Initializing the Dummy Database (Optional)

If you have created a dummy database using the module notarealdb, you will need to initialize it.

After creating the database file and placing some fake data, you will need to add the following lines to your index.js file:

//initialize the database
const db = new DataStore('<folder containing database>');
const studentDB = db.collection('<filename>');

Note! Make sure to update '<folder containing database>' and '<filename>' from the previous snippet of code.

Create a schema.graphql File To Define Schema

Now, create a schema.graphql file. This file will contain all the information to define the database and its entities and the queries you will require.

Before getting into how to write the file, it is important to distinguish exactly what you will be requiring. As explained above, CRUD APIs should have the create, read, update and delete functionalities. To add these functionalities, you need to structure the function based on the GrapQL query type.

GraphQL has three major types: Query, Mutation, and Subscription. For this guide, we will be using Query and Mutation.

For all read functions, you will require a resolver of type Query. This will include the read functionality.

Also, you will require a resolver of type Mutation for all the write functions. This will include our Create, Delete, and Update functionalities.

To create a schema.graphql file, create a new file with a .graphql extension in the same directory as the index.js file.

In this file, first, define the entities. In our case, we have only one entity, Student:

type Student {
    id: ID!             #! means it cannot be null and is its identifier 
    name: String
    age: Int
}

After defining the entity, list all the queries and mutations and the arguments required to execute them successfully. Since we are making a CRUD API, the following are the resolvers and their arguments written in GraphQL:

type Query {
    listStudents: [Student]
    getStudent(id: ID!): Student #Student does not have ! as it can be                                                        return as null
}

type Mutation {
    createStudent(name: String, age: Int): Student!,  
    deleteStudent(id: ID!): Student,
    updateStudent(id: ID!, name: String, age: Int): Student
}

Create Resolver

In the previous code, the mutation createStudent(name: String, age: Int) allows you to add an entry to your database. It returns a Student object to confirm that you have added a student to your database.

Read Resolver

To read data, the code uses two queries getStudent(id: ID!) and listStudent[Student]. Both of these queries allow you to read the data individually by providing an id or accessing all records respectively.

Update Resolver

To update data, the code uses the mutation updateStudent(id: ID!, name: String, age: Int). The id is used to identify the entry to be updated, and then the information such as name and age is changed. It then returns the updated student information.

Delete Resolver

To delete an entry, the code uses the mutation deleteStudent(id: ID!) to find the entry using the id and delete the record associated. The deleted entry is then returned.

After adding every type, your schema.graphql file should look something like this:

type Query {
    listStudents: [Student]
    getStudent(id: ID!): Student       #Student does not have ! as it can be return as null
}

type Mutation {
    createStudent(name: String, age: Int): Student!,       
    deleteStudent(id: ID!): Student,
    updateStudent(id: ID!, name: String, age: Int): Student
}

type Student {
    id: ID!                  #! means it cannot be null and it's its identifier 
    name: String
    age: Int
}

After creating the schema file, you will need to build a schema for this inside the index.js file. You can use the fs.readFileSync function to read the file and use the same function as an argument to the buildSchema() method.

One thing to note is that since buildSchema() takes a string as an argument, you will need to convert the stream generated when using fs.readFileSync into a string using the toString() method.

Add this line to your index.js file:

// Construct a schema, using GraphQL schema language
var schema = buildSchema(fs.readFileSync("schema.graphql").toString());

Define Resolvers in the index.js file

The resolvers that you listed in the schema.graphql will have to be defined for them to work as intended. Defining these resolvers can be thought of as writing a query for the particular database the API is connected to.

It’s important to know that since one query or mutation maps to exactly one resolver, you should name your resolver and the query or mutation the same. Add the following piece of code to your index.js file:

// root provides a resolver function for each API endpoint or CRUD functionality
var root = {

listStudents: () => {
    return studentDB.list()
},
getStudent: (argument) => {
    return studentDB.get(argument.id)
},
createStudent: (argument) => {
    const studentStuff = {
        id: argument.id,
        name: argument.name,
        age: argument.age 
    }
    return studentDB.create(studentStuff);
},
deleteStudent: (argument) => {
    const studentObj = studentDB.get(argument.id);
    studentDB.delete(argument.id);
    
    return studentObj;
},
updateStudent: (argument) => {
    const studentStuff = {
        id: argument.id,
        name: argument.name,
        age: argument.age 
    }
    studentDB.update(studentStuff);
    const studentObj = studentDB.get(argument.id);
    
    return studentObj;
}
};

The root variable acts as a defining function for all the CRUD functionalities or API endpoints. Inside root, you can define each endpoint and consider them as methods. For example, suppose deleteStudent() is called. In that case, the code written inside the root for deleteStudent() will be executed, which will then make changes to your database as defined by the schema.

Since we are using a dummy database, it is important to specify that all the code written above inside root is only pertinent to the dummy database. Write queries inside root for databases made with SQL, MongoDB, or any other query language.

After adding all the code explained above, your index.js file should look like this:

//importing relevant libraries

const express = require('express');              
const { graphqlHTTP } = require('express-graphql');
const { buildSchema } = require('graphql');
const fs = require("fs");
const { DataStore } = require('notarealdb');
const { report } = require('process');

//initialize the database
const db = new DataStore('database');
const studentDB = db.collection('studentData');

// Construct a schema, using GraphQL schema language
const schema = buildSchema(fs.readFileSync("schema.graphql").toString());  //returns a buffer but buildschema needs string so we convert to string


// root provides a resolver function for each API endpoint or CRUD functionality
const root = {
listStudents: () => {
    return studentDB.list()
},
getStudent: (argument) => {
    return studentDB.get(argument.id)
},
createStudent: (argument) => {
    const studentStuff = {
        id: argument.id,
        name: argument.name,
        age: argument.age 
    }
    return studentDB.create(studentStuff);  
},
deleteStudent: (argument) => {
    const studentObj = studentDB.get(argument.id);
    studentDB.delete(argument.id);
    
    return studentObj;  
},
updateStudent: (argument) => {
    const studentStuff = {
        id: argument.id,
        name: argument.name,
        age: argument.age 
    }
    studentDB.update(studentStuff);
    const studentObj = studentDB.get(argument.id);
    
    return studentObj;
}
};

//Creating a GraphQL API Server

const app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');

Test the CRUD API

Now, your API should be ready for testing!

To test your API, first, run your Node.js application by running the following command:

node index.js

After running the above command, you will see a localhost URL in the terminal, which will take you to a GraphiQL page. The GraphiQL page allows you to run your queries and mutations. You will see live changes to your data by opening the database.js file you created (if you created a dummy database) or by checking the database you connected. 

It is important to note that you can only run a single query or a single mutation at a time on the GraphiQL page. 

Wrapping Up

Congratulations! You created a CRUD API using Node.js and GraphQL. You learned how to create and define resolvers. You also learned how to create a dummy database, allowing you to practice your skills before using a real database.

Was this post helpful?

Please, let us know your opinions by replying on Twitter of Become A Better Programmer.