How to Build a GraphQL API With Node.js and Docker

Why GraphQL?

GraphQL is a language that enables you to provide a complete and understandable description of the data in your API. Furthermore, it gives clients the power to ask for exactly what they need and nothing more. The project’s official website can be found here.

Advantages of GraphQL

✔️  GraphQL is declarative: Query responses are decided by the client rather than the server. A GraphQL query returns exactly what a client asks for and no more.

✔️  GraphQL is compositional: A GraphQL query itself is a hierarchical set of fields. The query is shaped just like the data it returns. It is a natural way for product engineers to describe data requirements.

✔️ GraphQL is strongly-typed: A GraphQL query can be ensured to be valid within a GraphQL-type system at development time allowing the server to make guarantees about the response. This makes it easier to build high-quality client tools

Building Blocks of a GraphQL API

A GraphQL API has four building blocks:

1. Schema

It is defined at the server in the form of objects. Each object corresponds to data types such that they can be queried upon. For example:

type User {
id: ID!
name: String
age: Int }

The schema above defines the shape of a user object with a required field id denoted by the ! sign. Other fields such as the name which is of type string and age which is of type integer are also included. This also validates the schema when querying for the data.

  • typeDefs:  The definition of our schema of what we can expect from queries and mutations.
  • Resolvers: Instead of the expectation of fields or required parameters, here we define the functions and behaviors of how should the queries and mutations would work.
  • Queries: The “gets” that we want to read from the server.
  • Mutations: Our requests that are going to affect any data that we have on our own server.

Let’s get started with creating GraphQL⬇️

Setting Up The Project

Step 1. Initiate the project

npm init -y

Step 2. Install the dependencies

npm i

graphql express express-graphql

Step 3. Create an index.js file on the project root

var express = require('express');
var express_graphql = require('express-graphql');
var { buildSchema } = require('graphql');
// GraphQL schema
var schema = buildSchema(`
type Query {
message: String
}
`);
// Root resolver
var root = {
message: () => 'Hello World!'
};
// Create an express server and a GraphQL endpoint
var app = express();
app.use('/graphql', express_graphql.graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}),);
app.listen(4000, () => console.log('Express GraphQL Server Now Running On http://localhost:4000/graphql'));

Now, let’s test our app locally by running the following command:

npm start

Next, let’s stop the server by running

Ctrl + C

Now, open the URL http://localhost:4000/graphql on the browser

Now you can run the simple query on the GraphQL playground

{
message
}

Creating a Dockerfile

Create an empty file called Dockerfile:

touch Dockerfile

Open the Dockerfile in your favorite text editor

The first thing we need to do is define what image we want to build from. Here we will use the latest LTS (long-term support) version 16 of node available from the Docker Hub:

FROM node:16

Next, we create a directory to hold the application code inside the image, this will be the working directory for your application:

# Create app directory
WORKDIR /usr/src/app

This image comes with Node.js and NPM already installed so the next thing we need to do is to install your app dependencies using the npm binary. Please note that if you are using npm version 4 or earlier a package-lock.json the file will not be generated.

# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production

Note that, rather than copying the entire working directory, we are only copying the package.json file. This allows us to take advantage of cached Docker layers. Furthermore, the npm ci command, specified in the comments, helps provide faster, more reliable, reproducible builds for production environments. You can read more about this here.
To bundle your app’s source code inside the Docker image, use the COPY instruction:

# Bundle app source
COPY . .

Your app binds to port 8080 so you’ll use the EXPOSE instruction to have it mapped by the docker daemon:

EXPOSE 8080

Last but not least, define the command to run your app using CMD which defines your runtime. Here we will use node index.js to start your server:

CMD [ "node", "index.js" ]

Your Dockerfile should now look like this:

FROM node:16
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
# A wildcard is used to ensure both package.json AND package-lock.json are copied
# where available (npm@5+)
COPY package*.json ./
RUN npm install
# If you are building your code for production
# RUN npm ci --only=production
# Bundle app source
COPY . .
EXPOSE 8080
CMD [ "node", "server.js" ]

Create a .dockerignore file

Create a .dockerignore file in the same directory as your Dockerfile with the following content:

node_modules
npm-debug.log

This will prevent your local modules and debug logs from being copied onto your Docker image and possibly overwriting modules installed within your image.

Building your image

Go to the directory that has your Dockerfile and run the following command to build the Docker image. The -t flag lets you tag your image so it’s easier to find later using the docker images command:

docker build . -t <your username>/nodejs-graphql-docker
Your image will now be listed by Docker:
$ docker images
# Example
REPOSITORY TAG ID CREATED
node 16 3b66eb585643 5 days ago
<your username>/nodejs-graphql-docker latest d64d3505b0d2 1 minute ago

Run the image

Running your image with -d runs the container in detached mode, leaving the container running in the background. The -p flag redirects a public port to a private port inside the container. Run the image you previously built:

docker run -p 49160:8080 -d <your username>/nodejs-graphql-docker

Print the output of your app:

# Get container ID
$ docker ps
# Print app output
$ docker logs <container id>
# Example
Running on http://localhost:8080

If you need to go inside the container you can use the exec command:

# Enter the container
$ docker exec -it <container id> /bin/bash

Test

To test your app, get the port of your app that Docker mapped:

$ docker ps
# Example
ID IMAGE COMMAND ... PORTS
ecce33b30ebf <your username>/nodejs-graphql-docker:latest npm start ... 49160->8080

In the example above, Docker mapped the 8080 port inside of the container to the port 49160 on your machine.
Now you can open http://localhost:4000/ on the browser

Shut down the image

In order to shut down the app we started, we run the kill command. This uses the container’s ID, which in this example was ecce33b30ebf.

 # Kill our running container
$ docker kill <container id>
<container id>
# Confirm that the app has stopped
$ curl -i localhost:49160
curl: (7) Failed to connect to localhost port 49160: Connection refused

We hope this tutorial helped you get up and running a simple Node.js application on Docker.

coma

Conclusion

Building a GraphQL API with Node.js and Docker provides a powerful and scalable solution for modern web development.

By following the steps outlined in this tutorial, developers can create a GraphQL API. By leveraging the capabilities of Node.js and Docker, developers can create high-performance APIs that are capable of handling a variety of use cases and scaling to meet the needs of their users.

Keep Reading

Keep Reading

Mindbowser is excited to meet healthcare industry leaders and experts from across the globe. Join us from Feb 25th to 28th, 2024, at ViVE 2024 Los Angeles.

Learn More

Let's create something together!