In this tutorial, we will walk through the process of creating a GraphQL server using Node.js and Express. By the end of this guide, you will have a basic understanding of how to set up a GraphQL server that can handle queries and mutations.

Start by creating a new Node.js project if you haven’t done so already:

npm init --y

This command will create a package.json file, which is necessary for working with npm packages.

Next, install the required npm packages: express, graphql, and express-graphql:

npm install express graphql express-graphql

Now, let’s create the app.js file and start by initializing the Express server:

const express = require('express')
const app = express()

app.listen(3000, () => {
  console.log('App listening on port 3000')
})

To add GraphQL functionality, we need to include the express-graphql middleware. We will apply this middleware to a specific route, in this case, the /graphql route:

const express = require('express')
const graphqlHTTP = require('express-graphql')
const app = express()

app.use('/graphql', graphqlHTTP())

app.listen(3000, () => {
  console.log('App listening on port 3000')
})

Before we move forward, we need to define the schema for our GraphQL server. Let’s create a schema.js file and require the necessary objects from the graphql package:

const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

Next, we’ll define the schema by initializing a new GraphQLSchema instance. This instance should have a query property, which is an instance of a GraphQLObjectType object:

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    //...
  }),
})

module.exports = schema

Inside the query object, we must specify a name and a fields property. The fields property is an object that contains a set of properties, one for each field in our schema. Let’s set up a simple hello field:

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      hello: {
        type: GraphQLString,
        resolve() {
          return 'world'
        },
      },
    },
  }),
})

In this example, the resolve() method returns the string 'world', which means that when we ask for the hello field, we’ll get that string in response.

Here is the content of the complete schema.js file:

const graphql = require('graphql')
const { GraphQLSchema, GraphQLObjectType, GraphQLString } = graphql

const schema = new GraphQLSchema({
  query: new GraphQLObjectType({
    name: 'RootQueryType',
    fields: {
      hello: {
        type: GraphQLString,
        resolve() {
          return 'world'
        },
      },
    },
  }),
})

module.exports = schema

Now, let’s go back to our app.js file. We need to require the schema.js file and add it to the graphqlHTTP constructor:

const schema = require('./schema.js')

app.use(
  '/graphql',
  graphqlHTTP({
    schema: schema,
  })
)

With this configuration, we can now test our GraphQL API using GraphiQL, a tool for testing GraphQL APIs. To enable GraphiQL, we need to pass an additional property to the graphqlHTTP constructor:

app.use(
  '/graphql',
  graphqlHTTP({
    schema: schema,
    graphiql: true,
  })
)

After running node app.js, you can access the http://localhost:3000/graphql URL in the browser to see GraphiQL in action. You can test the first API call by passing the following query:

{
  hello
}

The result should be:

{
  "data": {
    "hello": "world"
  }
}

Now, let’s build a more complex schema that includes nested types. We’ll use a blog post example where each post has a title, description, and an author. The author has a name.

First, let’s define the posts and authors data:

const posts = [
  {
    title: 'First post',
    description: 'Content of the first post',
    author: 'Flavio',
  },
  {
    title: 'Second post',
    description: 'Content of the second post',
    author: 'Roger',
  },
]

const authors = {
  Flavio: {
    name: 'Flavio',
    age: 36,
  },
  Roger: {
    name: 'Roger',
    age: 7,
  },
}

Next, we’ll define three GraphQLObjectType instances:

  • authorType, which defines the author data
  • postType, which defines the post data
  • queryType, the main type

Let’s start with the authorType. An author has a name and an age:

const authorType = new GraphQLObjectType({
  name: 'Author',
  fields: {
    name: {
      type: GraphQLString,
    },
    age: {
      type: GraphQLInt,
    },
  },
})

Next is the postType. A post has a title, a description (both strings), and an author. The author field is of type authorType, which we just defined. We also need to provide a resolver function for the author field. In this case, we retrieve the author data from the authors object based on the author property of the source:

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    title: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    author: {
      type: authorType,
      resolve: (source, params) => {
        return authors[source.author]
      },
    },
  },
})

Lastly, we create the queryType, which is the root type that will be added to the schema. It includes two fields:

  • post, which retrieves a single blog post identified by an id
  • posts, which retrieves the list of posts

Both fields have a resolver function that retrieves the data from the posts array:

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    post: {
      type: postType,
      args: {
        id: { type: GraphQLInt },
      },
      resolve: (source, { id }) => {
        return posts[id]
      },
    },
    posts: {
      type: new GraphQLList(postType),
      resolve: () => {
        return posts
      },
    },
  },
})

Here, we use the GraphQLList type to indicate that posts is a list of postType objects.

Finally, we add the queryType to our schema:

const schema = new GraphQLSchema({
  query: queryType,
})

The complete code is as follows:

const graphql = require('graphql')
const {
  GraphQLSchema,
  GraphQLObjectType,
  GraphQLString,
  GraphQLList,
  GraphQLInt,
} = graphql

const posts = [
  {
    title: 'First post',
    description: 'Content of the first post',
    author: 'Flavio',
  },
  {
    title: 'Second post',
    description: 'Content of the second post',
    author: 'Roger',
  },
]

const authors = {
  Flavio: {
    name: 'Flavio',
    age: 36,
  },
  Roger: {
    name: 'Roger',
    age: 7,
  },
}

const authorType = new GraphQLObjectType({
  name: 'Author',
  fields: {
    name: {
      type: GraphQLString,
    },
    age: {
      type: GraphQLInt,
    },
  },
})

const postType = new GraphQLObjectType({
  name: 'Post',
  fields: {
    title: {
      type: GraphQLString,
    },
    description: {
      type: GraphQLString,
    },
    author: {
      type: authorType,
      resolve: (source, params) => {
        return authors[source.author]
      },
    },
  },
})

const queryType = new GraphQLObjectType({
  name: 'Query',
  fields: {
    post: {
      type: postType,
      args: {
        id: { type: GraphQLInt },
      },
      resolve: (source, { id }) => {
        return posts[id]
      },
    },
    posts: {
      type: new GraphQLList(postType),
      resolve: () => {
        return posts
      },
    },
  },
})

const schema = new GraphQLSchema({
  query: queryType,
})

module.exports = schema

You can find the complete code on Glitch.

Tags: GraphQL, Node.js, Express, Schema, Query, Mutation