A Beginner’s Guide to GraphQL

Build your first GraphQL API using Node.js and Apollo.

Advaith Malka
Published in
12 min readJan 25, 2021


The GraphQL logo (Credit: Aaron Ortbals)

Released in 2015 by Facebook, GraphQL has offered a new, promising alternative to the traditional REST API. Since then, many companies, including GitHub, Shopify, and Intuit, have adopted GraphQL into their ecosystems. While REST APIs might not become obsolete soon, GraphQL is quickly gaining popularity and love from many developers. As a result, the demand for GraphQL has increased drastically and is projected to grow exponentially over the new decade. By the end of this article, you will have a good understanding of how GraphQL works and how you can build APIs with it using Node.js. Throughout this tutorial, we will be building a fully functional GraphQL API that will add and query “users” to an array.

Note: In production, you should be querying and mutating a database (like MongoDB or Firebase), but for simplicity’s sake, we will be using hard-coded data in this tutorial.

Prerequisites: This tutorial is for people who have a good understanding of JavaScript and Node.js. Prior GraphQL knowledge is recommended but not required to follow along.

What is GraphQL?

A demo of GraphQL from the official GraphQL website.

GraphQL or “Graph Query Language” is, just as the name suggests, a query language for APIs.
While SQL is a query language for managing relational databases, GraphQL is a query language that allows the client (frontend) to request data from an API.

Advantages of GraphQL

Why use GraphQL over a traditional REST API? Let us take a look at some advantages GraphQL provides over its RESTful counterpart:

  • One endpoint: With traditional REST APIs, you have to create specific endpoints based on the data you want to request. This makes scaling your API difficult because soon, you might find yourself having to manage tens, maybe hundreds, of routes that you will have to remember.
  • Fewer server requests: GraphQL allows you to make multiple queries and mutations with only one server request. This can be useful when your server only allows a limited number of requests a day.
  • Declarative data fetching: Unlike a REST API, GraphQL only fetches what you actually need. All you have to do is specify what fields to return.
  • Type system: GraphQL uses a type system to describe your data, which makes developing much easier. If you are a TypeScript fan, this is a win-win.
  • Self-documenting: GraphQL is self-documenting, meaning that all of your queries and mutations will automatically be documented by GraphQL.

There are still many more advantages (and disadvantages) of GraphQL that are valuable to know. I would recommend reading GraphQL: Everything You Need to Know by Weblab Technology to learn more about GraphQL (click below).

What is Apollo Server?

Note: Apollo server also has an express integration, but we will not need Express.js in this tutorial.

Since GraphQL is just a query language, we need a library that will take care of the boilerplate code for us. Luckily, such a library already exists.

Enter Apollo Server, a Node.js library that provides an easy-to-use GraphQL server right out of the box. Other GraphQL server libraries also exist like express-graphql, but Apollo Server allows for better scalability and features a larger community. Apollo Server also provides a neat GraphQL interface for performing queries and mutations during development.

Now that we got that out of the way, we can finally start building our GraphQL API.

Note: If you get lost anytime during this tutorial, you can always compare your code with the GitHub repository.

Step 1: Installing Dependencies

Note: Make sure you have a code editor installed like Visual Studio Code.

First, you will need to make a directory that will contain all of your server files. We can do that in the terminal like so:

mkdir graphql-server-tut
cd graphql-server-tut

Then, to initialize NPM in our project:

npm init -y

This should create a package.json file in your current directory.

Now, we can start installing the required dependencies for our server:

npm install apollo-server graphql

Here, we are installing two necessary dependencies.

  • apollo-server: allows us to create a GraphQL server with ease.
  • graphql: a required dependency for apollo-server.

Once these packages are installed, we can start programming our server.

Step 2: Creating Our Type Definitions

We first need to create an index.js file in the current directory. This file will act as the entry point into our server when we finish with the API.

Next, we need to import ApolloServer and gql from the apollo-server NPM module.

const { ApolloServer, gql } = require(“apollo-server”);

Now, since GraphQL is a typed language, we need to define our data with a schema.

Place the following code in the index.js file.

Here, after importing ApolloServer and gql, we create a multiline GraphQL string containing our schema. Most developers name their schema typeDefs because when we initialize ApolloServer later, we need to pass our schema to an object key with the same name.

There are a few key things to notice in this code snippet:

  • Our typeDefs are passed into a gql tag. This tag sterilizes our type definitions and makes them readable to Apollo Server. This also allows for auto-completion during development.
  • The Query type lists all the possible queries that can be performed by our server.
  • hello and randomNumber are two different queries.
  • We define the type of the returned value after a colon (:). In this case, hello returns a String type, and randomNumber returns an Integer type.
  • An exclamation mark (!) after the type indicates a return value is required.

Step 3: Creating Resolver Functions

Now we need to tell our server what to do or return when a specific query is called. We can solve this issue by creating resolver functions.

Copy the code below into your index.js file:

Let’s see what this code does line-by-line:

  • The resolvers should match our typeDefs. Just like we had a Query type in our typeDefs, we have a Query object in our resolvers.
  • The Query object contains the resolvers that correspond to our typeDefs. (Each query has a corresponding resolver function with the same name)
  • Whatever you return in the resolver, the query returns to the client.

Note: In the real-world, resolvers usually fetch data from a database and return it to the client. Unfortunately, fetching and mutating a database is beyond the context of this tutorial.

Step 4: Putting It All Together

It’s finally here! The step we all have been waiting for: it’s time to run our server.

First, we need to create an instance of ApolloServer and pass in our typeDefs and resolvers.

We can do that like so:

Let’s take a look at what is happening here:

  • First, we created an instance of ApolloServer (which was imported in step 2) and passed in our typeDefs and resolvers, which we made in steps 2 and 3.
  • Then, we start the server on port 8080 (default is 4000).

This is how your index.js file should look like now:

Our server is ready to go! In the terminal, run the index.js file by typing node index.

If you followed along correctly, you should see “GraphQL server running at http://localhost:8080/" logged into the console.

When you navigate to localhost:8080, you should see the GraphQL playground:

The GraphQL playground

This neat interface comes from the apollo-server module and allows you to perform queries straight to the server without connecting the frontend.

Note: The GraphQL playground won’t be available if your Node environment is set to production.

There are a few cool features to note when using the GraphQL playground:

  • On the right, you will see two tabs: the Schema and the Docs.
  • When your API grows in size, you can refer to the Schema tab to see all the available queries and mutations the server can perform.
  • Remember when I mentioned GraphQL was self-documenting? You can see the documentation GraphQL generated for your API in the Docs tab.
  • The GraphQL playground also allows you to add HTTP headers to your query or mutation. This is useful if you only want authorized users using your API.

Querying our API

Now that our server is all set up, we can send requests to it through the GraphQL playground.

To perform a query, paste the following code into the GraphQL playground:

query {

Here, we are calling both of our queries that we set up in the previous steps. Once you click the play button, you should see the data sent back corresponds with what our resolvers returned:

The data that returned by our GraphQL query.

The beauty of GraphQL is that you only get back what you specify; if you remove the hello query, it won’t show up in the data anymore:

GraphQL makes use of declarative data fetching.

Creating a More Advanced API

Now, since you hopefully know a little bit of how ApolloServer works, we are going to create an API that will be able to add and query users. This time, instead of only being able to query data, we will be able to add data as well.

Step 1: Creating Our “Database”

Instead of using an actual database like MongoDB or Firebase, we will use hardcoded data in this tutorial and store everything in an array.

First, we are going to create an array called users. Each user is going to have a firstName, a lastName, and an email field. If we wanted to, we could include some hardcoded data in the array like so:

Feel free to add hardcoded data to the array.

Step 2: Setting Up the typeDefs

Now, we need a way to query all the users in our “database.” Let’s update our typeDefs to allow for this function:

There are a couple of things to notice here:

  • The queryUsers query returns an array of objects (Hence the brackets).
  • We can create our own GraphQL types using the type keyword followed by the name of the type.
  • In curly-brackets ({}), we specify the fields that type will return (Our User type will return three fields: firstName, lastName, and email. All three are strings and required).

Step 3: Configuring Our Resolvers

We just need to add one more line of code to finish the query:

This new line of code creates a resolver function that, when invoked, will return the users array.

Step 4: Testing the Query

Our query will look a little bit different this time:

query {
queryUsers {
The data should look similar to this when queryUsers is called.

When we call the queryUsers query, we need to specify which fields we want the API to return in curly-brackets ({}). The code above returns all three fields, but if the client only needs the first and last name of each user, you could omit the email field to save bandwidth:

Only query what you need to increase speed and reduce bandwidth usage.

Adding Users to Our Array

Our API won’t be of much use if it only can show hardcoded users. In this section, we will allow our API to add users to the array as well.

Step 1: Adding a Mutation to Our typeDefs

Whenever you perform any other operations other than reading from a database (creating, updating, deleting), you should use GraphQL mutations.

All mutations must be in the GraphQL Mutation type:

I would take note of a few things from this snippet:

  • We are creating a new mutation named addUser
  • addUser takes in three arguments: firstName, lastName, and email. All three arguments are of type string and required (specified in the parentheses)
  • addUser returns a User type: an object containing the new user’s firstName, lastName, and email.

Step 2: Adding the addUser Resolver

Before we start writing the resolver, let’s plan out what it should accomplish.

First, the resolver will need to get the firstName, lastName, and email arguments from the mutation when we run it. Then, it will need to push that data as a new object to the users array. Lastly, we simply return the data that was passed into the mutation.

Note: When working with real databases, you should implement a try, catch block to handle errors that might occur.

Update your resolvers to match the following:

Add the Mutation section to your resolvers.

Let’s take a look at what this resolver will do:

  • Just like queries, mutations must match our typeDefs and go in the Mutation object.
  • Every resolver (not only mutations) has access to four parameters, which you can learn more about in the documentation. We only need the second argument for this resolver.
  • The second argument, args, will contain the firstName, lastName, and email of the new user. If you wish, console.log the args argument in the resolver function to see what data it contains.
  • Since our “database” is just an array of objects, we can simply push the args object to the users array.
  • Our mutation needs to return the new user that was created. We can do that by returning the args object.

That’s it! With just less than ten lines of code, our server is now able to add and query users! Now, let us take a look at how we can call GraphQL mutations.

Step 4: Calling Our Mutation

Calling mutations is very similar to calling queries in GraphQL:

mutation {
firstName: "John",
lastName: "Doe",
email: "john.doe@somemail.com"
) {

Tip: I would place the above GraphQL query in a new GraphQL Playground tab.

Notice a couple of things about the query above:

  • To signify a mutation, we use the mutation keyword instead of query.
  • We can pass arguments into a GraphQL query by specifying them inside of parentheses, just like a JavaScript function.
  • Here, we create a new user with the name of John Doe and the email of john.doe@somemail.com. Feel free to change the arguments if you wish.
  • Like our queryUsers query, we can choose what fields to return. Remember that this mutation returns only the new user that was created.

The data returned from the mutation should look something like this:

The data returned from the addUser mutation.

If you run the queryUsers query again, without restarting the server, you should see a new user added to the array:

“John Doe” has successfully been added to our users array!

Note: New data will be lost when you restart your server, so make sure you use a database in real-world applications.

Great! Our API is now able to query and add users to our array! To challenge yourself, I would recommend adding a couple more fields to each user. i.e. age, birthday, and favorite foods. If you are familiar with MongoDB or Firebase, try integrating a database with the API instead of storing data in an array.

Congratulations on building your first GraphQL API with Apollo Server! This tutorial just scratched the surface of what GraphQL can do. There is still much more to learn, such as subscriptions, fragments, directives, and much more! Whether you are looking for an alternative to a REST API or a new developer trying to expand your knowledge, GraphQL is a great choice, and I am certain you will enjoy working with it.

Happy coding!