DEV Community

loading...
Cover image for How to create a GraphQL server with ExpressJS and Apollo Server

How to create a GraphQL server with ExpressJS and Apollo Server

Rossano D'Angelo
Application engineer at The LEGO Group. I like reading manga. All my opinions are belong to me.
・5 min read

In this article, I'm going to show how to create a GraphQL Server using ExpressJS and Apollo Server.

Crete the project folder and install Apollo Server and Express

mkdir graphql-server-playground
cd graphql-server-playground
npm init -y
Enter fullscreen mode Exit fullscreen mode

The package.json file should be something like this

{
  "name": "graphql-server-playground",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
Enter fullscreen mode Exit fullscreen mode

Add description, keywords and author if you want.

In order to setup Express and Apollo Server, install the following dev dependencies

npm install --save-dev @babel/cli @babel/core @babel/node @babel/preset-env nodemon
Enter fullscreen mode Exit fullscreen mode

and the following dependencies

npm install apollo-server-express express graphql graphql-import graphql-tools
Enter fullscreen mode Exit fullscreen mode

Now, in the root folder of your project, create the src folder and the index.js file in it

import express from 'express';
import { ApolloServer, gql } from 'apollo-server-express';

const typeDefs = gql`
  type Query {
    hello: String
  }
`;

const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

app.listen({ port: 4001 }, () =>
  console.log(`🚀 Server ready at http://localhost:4001${server.graphqlPath}`)
);
Enter fullscreen mode Exit fullscreen mode

Update the package.json file

{
  ...
  "main": "src/index.js",
  "scripts": {
    "start": "nodemon --exec babel-node -- ./src/index.js"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Nodemon is a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.

Before running the server, we have to create a .babelrc file in the root folder of the project

{
  "presets": ["@babel/preset-env"]
}
Enter fullscreen mode Exit fullscreen mode

This file tells NodeJS we are using Babel 7 and that the code has to be transpiled for the browser.

Now start the server with npm start

npm start

> graphql-server-playground@1.0.0 start graphql-server-playground
> nodemon --exec babel-node -- ./src/index.js

[nodemon] 2.0.7
[nodemon] to restart at any time, enter `rs`
[nodemon] watching path(s): *.*
[nodemon] watching extensions: js,mjs,json
[nodemon] starting `babel-node ./src/index.js`
🚀 Server ready at http://localhost:4001/graphql
Enter fullscreen mode Exit fullscreen mode

The GraphQL server is now running on your machine at http://localhost:4001/graphql

The Playground

The Playground is where you can run your queries and check for the documentation or your schema.

Alt Text

Alt Text

Alt Text

The only query available now is the following

query {
  hello
}
Enter fullscreen mode Exit fullscreen mode

as you can see in the code, it corresponds to this

const typeDefs = gql`
  type Query {
    hello: String
  }
`;
Enter fullscreen mode Exit fullscreen mode

Each query has to have a resolver. The resolver is a function that defines what the query returns

const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};
Enter fullscreen mode Exit fullscreen mode

In this case, the resolver for the hello query returns a string, Hello world!

If you want to learn more about GraphQL queries and resolvers, take a look here.

Defining the schema

Before defining the schema, we have to decide what kind of data this server will provide to the client.

Let's say we want to store and serve information about our address book. So the data I exptect to have is a list of people whith names, phone numbers and email addresses. Since we don't have a real database for now, let's mock it in the index.js file

...
const persons = [
    {
        id: 1,
        name: "Ash Ketchum",
        phone_number: "+440000000000",
        email: "ash@gmail.com",
    },
    {
        id: 2,
        name: "Professor Oak",
        phone_number: "+441111111111",
        email: "proak@gmail.com",
    },
    {
        id: 3,
        name: "Gary Oak",
        phone_number: "+442222222222",
    },
];
...
Enter fullscreen mode Exit fullscreen mode

This will be our temporary database.

Now the schema:

const typeDefs = gql`
  type Person {
      id: Int!
      name: String!
      email: String
  }
  type Query {
    person(id: Int!): Person
  }
`;
Enter fullscreen mode Exit fullscreen mode

Let's have a closer look.

Since we already defined the structure of the data, we are pretty sure that what we will receive from the database will have this shape:

  • id: Int! (an integer, non nullable)
  • name: String! (a string, non nullable)
  • email: String (a string, nullable)

The query person(id: Int!): Person wants an id as parameter (note the id is non nullable - it can't be null or we won't be able to retrieve anything!) and it returns a Person (an object defined by the Person type). Since it may happen the person we search for is not present in the database, the return type of this query is nullable (it can returns null).

Let's run the query in the Playground

query {
  person(id: 1) {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

Alt Text

Writing a new query

const typeDefs = gql`
  ...
  type Query {
    ...
    persons: [Person]!
  }
`;
Enter fullscreen mode Exit fullscreen mode
const resolvers = {
  Query: {
    ...
    persons: (_, __, ___) => persons,
  },
};
Enter fullscreen mode Exit fullscreen mode

Pretty straight-forward, right? The new query persons returns the whole persons array. Let's run the new query on the Playground

Alt Text

Writing a mutation

A Mutation is a function used to create, update and delete data in GraphQL. If you think at CRUD, mutations represent the C(reate) U(update) D(elete).

Let's create a new type PersonInput and another root type Mutation

const typeDefs = gql`
  ...
  input PersonInput {
    id: Int!
    name: String!
    email: String
  }
  type Mutation {
    createPerson(person: PersonInput!): Person
  }
`;
Enter fullscreen mode Exit fullscreen mode

The function createPerson accepts a non nullable object of type PersonInput and returns another object of type Person. It's just a case the two types Person and PersonInput are the same.

The mutation then is something like

const resolvers = {
  ...
  Mutation: {
    createPerson(_, { person }, __) => {
      persons.push(person);
      return person;
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

For simplicity's sake we are entering manually the ID paying attention to not reuse the same ID twice (or the query person will unexpectedly return two results!).

Let's use the mutation on the Playground

mutation {
  createPerson(person: {id: 5, name: "Brock", email: "brock@gmail.com"}) {
    id
    name
    email
  }
}
Enter fullscreen mode Exit fullscreen mode

Alt Text

We can also run the persons query to see how the list of people is updated

Alt Text

Please remember that we are using an object as temporary database and that you will lose the data you add/change/remove via mutations once you restart the server! If you want some persistency, think about connecting the GraphQL server to a data source. I won't do that in this article.

Improving the code style with Prettier

Install Prettier with

npm install prettier --save-dev
Enter fullscreen mode Exit fullscreen mode

Update your package.json file as follows

"scripts": {
  ...
  "prettier": "prettier --write ."
},
Enter fullscreen mode Exit fullscreen mode

Create a file .prettierrc.json in the root folder of your project

{
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": false,
  "singleQuote": true
}

Enter fullscreen mode Exit fullscreen mode

This file contains the configuration of Prettier and the rules it will apply to your code.

Create a second file, .prettierignore and copy-paste the content of your .gitignore. This file will simply tell Prettier which files to ignore while formatting the code.

Now when you run npm run prettier it will apply the formatting rules to all files in the root directory and sub-directories. You can change this replacing

"prettier": "prettier --write ."
Enter fullscreen mode Exit fullscreen mode

with

"prettier": "prettier --write ./src/**"
Enter fullscreen mode Exit fullscreen mode

In this case Prettier will format files in the src folder and in its sub-folders.

It's a wrap

That's it, you made it! Now you have a GraphQL server running locally and a very useful Playground to execute queries! 🚀

Here's the repository with all the code we wrote in this article https://github.com/rossanodan/graphql-server-playground. Feel free to fork it and use it as your own or as a template.

Discussion (0)