How to Create GraphQL API Server in NodeJS

Traditional REST API style can get cumbersome when the complexity of APIs increases. GraphQL provides a modern approach to client-server communication and helps the development of web applications in a more efficient way.

In this post, we’ll learn what GraphQL is, how GraphQL works, and how we can implement it in NodeJS by building a simple GraphQL API Server application using expressJS.

Learn about building CRUD API in NodeJS here.

Introduction to GraphQL

GraphQL is an open-source query language for APIs that provides a runtime for fulfilling queries with the existing data. GraphQL can not just be used to query from databases but can be used to query data from any number and type of sources.

According to GraphQL docs, GraphQL gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools.

GraphQL API Server in NodeJS

Assuming you have NodeJS installed in your system. We’ll quickly get started with creating a new folder and initializing a Node project using npm.

mkdir learning-graphql && cd learning-graphql
npm init -y

Now we can install our dependencies

npm install express express-graqhql graphql

Create a server.js file in the project folder and write imports for the installed packages

const express = require("express")
const { graphqlHTTP } = require("express-graphql")
const { graphql, buildSchema } = require("graphql")

graphqlHTTP will help us construct an express application using GraphQL schema. Now define the schema, a rootValue, and create a server with a route at /graphql. The schema describes the structure of the data available to us and rootValue is the initial value passed into the entry point of our query.

const app = express()
const port = 5000

const schema = new buildSchema(`
  type Query {
    hello: String
  }
`)

const rootValue = { hello: () => "Hello world!" };

app.use("/graphql", graphqlHTTP({
  schema: schema,
  rootValue: rootValue,
  graphiql: true
}))
app.listen(port? port: 5050, () => console.log("Server is listening on Port:", port))

The rootValue constant here is just an object with a key of hello that maps to an anonymous function returning the string “Hello World!”

Now run server.js using the below command in the terminal.

node server.js 

Graphiql comes pre-packaged with the GraphQL package. It is an interactive, in-browser GraphQL IDE that will help us in validating our GraphQL APIs and queries without having to build a front end. In the next few steps. we will be using Graphiql to test our APIs and write our queries.

We have used ExpressJS in this application for the sake of simplicity however GraphQL is also used with ApolloServer. Learn more about ApolloServer here.

GraphQL Queries and Mutations

GraphQL can be compared to RESTful API. GraphQL uses the convention of Queries and Mutations instead of GET, POST, or similar HTTP methods for getting and manipulating data.

GraphQL Query

Queries are similar to HTTP GET requests. Queries determine which data to read.

In the above section, we created an instance of the GraphQLSchema object and assigned it to the schema constant, and passed a rootValue to graphqlHTTP. Now we will try to make a query to our server. Open your browser and visit http://localhost:5000/graphql to get access to Graphiql.

Type the following query in the left editor

query {
  hello
}

Click on the execute query button and observe the output.

Graphiql IDE Output

We get a data object with a key-value pair of the rootValue and the value of our rootValue resolver function in the output tab. This gives us a brief idea of how GraphQL query works in general.

GraphQL Mutation

Mutations can be compared with HTTP POST or PUT requests. Mutations determine how data is written to the server. We will be understanding mutations by seeing them in action once we start building our main example application in the next section.

GraphQL Object Type

In most cases, we won’t just be returning simple strings or integers from GraphQL APIs but might want objects that can perform some actions. GraphQL provides us with different predefined object types and also objects for creating custom object types.

We will be building a simple application that queries the data of singers and the songs written by them and build mutations to add new singers and songs to the data source

We have used the existing learning-graphql repository for this demo but you may create a new one and install the above-mentioned dependencies.

Create a data.js file and paste the following data.

// data.js
exports.singers = [
  { id: 1, name: "Taylor Swift" },
  { id: 2, name: "Ed Sheeran" },
  { id: 3, name: "Harry Styles" },
  { id: 4, name: "Bruno Mars" }
]

exports.songs = [
  { id: 1, name: "Bad Blood", singerId: 1 },
  { id: 2, name: "You belong with me", singerId: 1 },
  { id: 3, name: "Perfect", singerId: 2 },
  { id: 4, name: "Shape of you", singerId: 2 },
  { id: 5, name: "Photograph", singerId: 2 },
  { id: 6, name: "Watermelon sugar", singerId: 3 },
  { id: 7, name: "Talking to the Moon", singerId: 4 },
  { id: 8, name: "Sign of the times", singerId: 3 },
  { id: 9, name: "Just the way you are", singerId: 4 },
  { id: 10, name: "Cardigan", singerId: 1 },
]

Now import these data along with some other GraphQL Object Types to form our schema, types, queries, and mutations. Our server.js will look something like this after the imports.

// server.js
const express = require("express")
const { graphqlHTTP } = require("express-graphql")
const { songs, singers } = require("./data");
const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLList,
  GraphQLInt,
  GraphQLNonNull
} = require("graphql")

const app = express()
const port = 5000

const schema = new GraphQLSchema({
  query: RootQueryType,
  mutation: RootMutationType,
})

app.use("/graphql", graphqlHTTP({
  schema: schema,
  graphiql: true
}))
app.listen(port? port: 5050, () => console.log("Server is listening on Port:", port))

We have defined a schema here using the GraphQLSchema object but we haven’t defined our RootQueryType and RootMutationType yet. We will be describing them after we define our custom SongType and SingerType.

Paste the following code after we define the port.

const SongType = new GraphQLObjectType({
  name: "Song",
  description: "This represents a song written by an author",
  fields: () => ({
    id: { type: GraphQLNonNull(GraphQLInt) },
    name: { type: GraphQLNonNull(GraphQLString) },
    singerId: { type: GraphQLNonNull(GraphQLInt) },
    singer: {
      type: SingerType,
      resolve: (song) => {
        return singers.find(singer => singer.id == song.singerId)
      }
    }
  })
})

const SingerType = new GraphQLObjectType({
  name: "Singer",
  description: "This represents an singer of a song",
  fields: () => ({
    id: { type: GraphQLNonNull(GraphQLInt) },
    name: { type: GraphQLNonNull(GraphQLString) },
    songs: {
      type: new GraphQLList(SongType),
      resolve: (singer) => {
      return songs.filter(song => song.singerId == singer.id)
      }
    }
  })
})

SongType and SingerType both use GraphQLObjectType to create a new object which has properties like name, description, fields, etc. Name and description are self-explanatory, and fields form an important property. Fields are object-specific attributes that hold some value. Our SongType and SingerType both have attributes like id and name with GraphQL-specific types.

Now SongType and SingerType have a unique property singer and songs respectively with a resolve function that simply denotes what to return when the attribute songs are queried. We must note that the type property of the singer in SongType and songs in SingerType refers to SingerType and SongType respectively.

Defining RootQueryType using GraphQLObjectType

We can start defining our RootQueryType once all our object types have been sorted. We use the GraphQLObjectType to create the RootQueryType and add in the resolve functions accordingly. Our resolve functions read selective data from the array as of now but in real-world applications, it can be used to query databases, APIs, or anything that suits our needs.

const RootQueryType = new GraphQLObjectType({
  name: "Query",
  description: "Root Query",
  fields: () => ({
    song: {
      type: SongType,
      description: "A single song",
      args: {
        id: { type: GraphQLInt }
      },
      resolve: (parents, args) => songs.find(song => song.id === args.id)
    },
    songs: {
      type: new GraphQLList(SongType),
      description: "List of Songs",
      resolve: () => songs
    },
    singers: {
      type: new GraphQLList(SingerType),
      description: "List of all Singers",
      resolve: () => singers
    },
    singer: {
      type: SingerType,
      description: "A single author",
      args: {
        id: { type: GraphQLInt }
      },
      resolve: (parents, args) => singers.find(singer => singer.id === args.id)
    }
  })
})

The song and singer field above has a property of args which denotes that our query field accepts an id parameter of type GraphQLInt while songs and singers simply return the arrays.

Defining RootMutationType using GraphQLObjectType

Just like RootQueryType, we can create a RootMutationType that will be sending requests to make changes to our array. Here we will be adding new songs and singers to our array.

const RootMutationType = new GraphQLObjectType({
  name: "Mutation",
  description: "Root Mutation",
  fields: () => ({
    addSong: {
      type: SongType,
      description: "Add a song",
      args: {
        name: { type: GraphQLNonNull(GraphQLString) },
        singerId: { type: GraphQLNonNull(GraphQLInt) },
      },
      resolve: (parent, args) => {
        const song = {
          id: songs.length + 1,
          name: args.name,
          singerId: args.singerId
        }
        songs.push(song)
        return song
      }
    },
    addSinger: {
      type: SingerType,
      description: "Add an singer",
      args: {
        name: { type: GraphQLNonNull(GraphQLString) }
      },
      resolve: (parent, args) => {
        const singer = {
          id: singers.length + 1,
          name: args.name,
        }
        singers.push(singer)
        return singer
      }
    }
  })
})

GraphQL API Server Application

If we had followed the above steps carefully our server.js will look something like this.

// server.js

const express = require("express")
const { graphqlHTTP } = require("express-graphql")
const { songs, singers } = require("./data");
const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLList,
  GraphQLInt,
  GraphQLNonNull
} = require("graphql")

const app = express()
const port = 5000

const SongType = new GraphQLObjectType({
  name: "Song",
  description: "This represents a song written by an author",
  fields: () => ({
    id: { type: GraphQLNonNull(GraphQLInt) },
    name: { type: GraphQLNonNull(GraphQLString) },
    singerId: { type: GraphQLNonNull(GraphQLInt) },
    singer: {
      type: SingerType,
      resolve: (song) => {
        return singers.find(singer => singer.id == song.singerId)
      }
    }
  })
})

const SingerType = new GraphQLObjectType({
  name: "Singer",
  description: "This represents an singer of a song",
  fields: () => ({
    id: { type: GraphQLNonNull(GraphQLInt) },
    name: { type: GraphQLNonNull(GraphQLString) },
    songs: {
      type: new GraphQLList(SongType),
      resolve: (singer) => {
      return songs.filter(song => song.singerId == singer.id)
      }
    }
  })
})

const RootQueryType = new GraphQLObjectType({
  name: "Query",
  description: "Root Query",
  fields: () => ({
    song: {
      type: SongType,
      description: "A single song",
      args: {
        id: { type: GraphQLInt }
      },
      resolve: (parents, args) => songs.find(song => song.id === args.id)
    },
    songs: {
      type: new GraphQLList(SongType),
      description: "List of Songs",
      resolve: () => songs
    },
    singers: {
      type: new GraphQLList(SingerType),
      description: "List of all Singers",
      resolve: () => singers
    },
    singer: {
      type: SingerType,
      description: "A single author",
      args: {
        id: { type: GraphQLInt }
      },
      resolve: (parents, args) => singers.find(singer => singer.id === args.id)
    }
  })
})

const RootMutationType = new GraphQLObjectType({
  name: "Mutation",
  description: "Root Mutation",
  fields: () => ({
    addSong: {
      type: SongType,
      description: "Add a song",
      args: {
        name: { type: GraphQLNonNull(GraphQLString) },
        singerId: { type: GraphQLNonNull(GraphQLInt) },
      },
      resolve: (parent, args) => {
        const song = {
          id: songs.length + 1,
          name: args.name,
          singerId: args.singerId
        }
        songs.push(song)
        return song
      }
    },
    addSinger: {
      type: SingerType,
      description: "Add an singer",
      args: {
        name: { type: GraphQLNonNull(GraphQLString) }
      },
      resolve: (parent, args) => {
        const singer = {
          id: singers.length + 1,
          name: args.name,
        }
        singers.push(singer)
        return singer
      }
    }
  })
})

const schema = new GraphQLSchema({
  query: RootQueryType,
  mutation: RootMutationType,
})

app.use("/graphql", graphqlHTTP({
  schema: schema,
  graphiql: true
}))
app.listen(port? port: 5050, () => console.log("Server is listening on Port:", port))

We can now run the command node server.js in the terminal and open HTTP://localhost:5000/graphql to test our GraphQL server.

Example Queries

We can perform the following queries in Graphiql and see the result.

Example 1:

query {
  #queries all songs and returns their id and name
  songs {
    id
    name
  }
}

Output:

{
  "data": {
    "songs": [
      {
        "id": 1,
        "name": "Bad Blood"
      },
      {
        "id": 2,
        "name": "You belong with me"
      },
      {
        "id": 3,
        "name": "Perfect"
      },
      {
        "id": 4,
        "name": "Shape of you"
      },
      {
        "id": 5,
        "name": "Photograph"
      },
      {
        "id": 6,
        "name": "Watermelon sugar"
      },
      {
        "id": 7,
        "name": "Talking to the Moon"
      },
      {
        "id": 8,
        "name": "Sign of the times"
      },
      {
        "id": 9,
        "name": "Just the way you are"
      },
      {
        "id": 10,
        "name": "Cardigan"
      }
    ]
  }
}

Example 2:

#queries for the name of song with id == 2
query {
  song(id: 2) {
    name
  }
}

Output:

"song": {
    "name": "You belong with me"
},

Example 3:

#queries for the singer with id == 3 and
  # the name of his/her songs
query {
  singer(id: 3) {
    id
    name
    songs {
      name
    }
  }
}

Output:

{
  "data": {
    "singer": {
      "id": 3,
      "name": "Harry Styles",
      "songs": [
        {
          "name": "Watermelon sugar"
        },
        {
          "name": "Sign of the times"
        },
        {
          "name": "As it was"
        }
      ]
    }
  }
}

Example Mutations

Similar to queries, we can also perform mutations and make changes to the data source. We must note that we won’t be able to see the changes made by us after we restart our server as we are interacting with Arrays in runtime in this application.

Example 1:

mutation {
  #mutation to add new song for a singer
  addSong(name: "As it was" singerId: 3) {
    singer {
      name
    }
  }
}

Output:

{
  "data": {
    "addSong": {
      "singer": {
        "name": "Harry Styles"
      }
    }
  }
}

Example 2:

mutation {
  #mutation to add new singer
  addSinger(name: "Anne Marie") {
    name
    id
  }
}

Output:

{
"data": {
 "addSinger": {
  "name": "Anne Marie",
   "id": 5
   }
  }
}

Conclusion

GraphQL is a powerful query language created by Facebook that provides us with a lot of features and functionalities to ease the process of building complex APIs.

In this post, we understood the basics of GraphQL, how the object type is an important concept in GraphQL, understood how we can use the GraphQL object type in NodeJS to build queries and mutations. Secondly, with the basics, we build a simple GraphQL API server that we can use to perform queries and mutations on.

Wondering what to read next? Learn how you can maximize the power of REST API using Amplification here.

References

https://graphql.org/graphql-js/object-types/

https://www.npmjs.com/package/express-graphql

Devdeep Ghosh
Devdeep Ghosh
Articles: 14