10 min read

david-torres-Ljz Wmk7t7g-unsplash

Integrating Apollo and Express to build a Node.js GraphQL API


Using ES6/7 powered by Babel

Written by Maximiliano Duthey, Full Stack Developer @ XOOR

GraphQL has been gathering a lot of attention lately and at XOOR we don’t like to let things pass by. So in some of our latest work we started experimenting with it and even started using it in production for some projects. In this post we’ll see how to build a very simple API using Express and Apollo as the GraphQL server. If you’re not familiar with GraphQL yet, in a few words it can be described as:

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, 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.

So basically it’s an alternative to RESTful APIs, and the biggest advantage it has is that you can ask for exactly what you need. Sometimes with REST you have an endpoint to get user information and you get a bunch of data that’s never used. With GraphQL you can say: “I want the email and the phone number from the user with ID 99”. This was a very stupid and high level explanation of GraphQL, so I encourage you to go ahead and read a bit about it if you’d like to know more about it.

If you want to see the code for this project, it’s hosted on GitHub -> https://github.com/xoor-io/express-apollo-api

Initial server setup

The first thing we’ll do then is create a directory wherever in your file system, cd into it and init an NPM project there using npm init .

In our example we’ll be using Express as the http server and Babel so that we can write awesome ES6 and ES7. Let’s start by installing a few Babel dependencies:

npm i -D @babel/cli @babel/core @babel/node @babel/preset-env @babel/register

And in the project root we’ll create a babel config file named .babelrc with the following contents:

{
 "presets": [
   ["@babel/preset-env", {
     "targets": {
       "node": "current"
     }
   }]
 ]
}

In this case we tell Babel to compile code into Javascript that the “current” Node.js version understands. Feel free to set there a specific version instead if you want to. More info about preset-env here.

Next on our list is a good friend of all Node.js devs: Nodemon. Go ahead and install it as a dev dependency:

npm i -D nodemon

Next thing is updating the start script on our package.json file. Since we’re coding with ES6/7, we need to use babel-node to run our code. So replace the default “start” script in the package.json file with the following one:

NODE_ENV=development nodemon src/app.js --exec babel-node

With this start script, when running npm start we’ll be lifting the server using nodemon and calling the app.js script with babel-node .

Now we’re ready to install express and cors to start building the server base structure:

npm i express cors

Then we create an src directory in the root of our project where all the API code will reside. Within this directory we’ll create 2 files: server.js and app.js. The first one will contain all the express app configuration and export it, so that it’s easier to create unit tests later. The latter will be the main entry point for the app and will import server.js to run the app.

The server.js file will look like this:

gist:xoor-io/72089cfda7c8eee4048601724cdc8e3d

Not very complex, specially for those familiar with Express. We initialize the app by using the express() function. Then we configure the cors middleware allowing all origins to query our API. This is for development purposes only, in production you MUST make sure that the allowed origins are trusted ones only. We also define a utility endpoint to check if the API is up and running and finally export the app, as well as two utility methods to change the app port and to start listening on the configured port.

And the app.js will import the previous code and call the listen() function to start the http server:

gist:xoor-io/d1087e85c3723959c622bb0e87cfdc36

Up to this point we’ll be able to run our server. So open up your console and run npm start . The app will be listening on the port 5000 by default. You can check that it’s working by using the GET /api/status endpoint.

So far we didn’t do anything special, we just created an express application with a dummy endpoint. Let’s start building cooler things by adding Apollo to our app.

Configuring Apollo GraphQL

First thing on our list will be installing a few needed dependencies:

npm i -D apollo-server-express body-parser graphql graphql-tools lodash

Next step is setting up a config dir where we’ll store some of our common configuration for the app. You can use other packages to handle this, we’ll just write a very simple module. So let’s create a config directory withinsrc and let’s create an index.js file there with the following contents:

gist:xoor-io/931f8a5641ab43f75b9e4f74853ca1d7

We’ll update our server.js file to use this config file later.

Now we’re ready to start integrating Apollo into our app. Let’s create a src/graphql directory and put an index.js file and a schema directory inside. The first one will contain the Apollo middleware configuration for our express server. The schema directory will contain all the GraphQL schema definitions.

With GraphQL it all relates to schema definitions. More about schemas and types here -> https://graphql.org/learn/schema/

Now open the src/graphql/index.js file and put the following code inside:

gist:xoor-io/36d0b506cd05dc7dde9916e1e3f85e59

Important things being done here:

  • We’re importing dependencies from apollo-server-express, which work as regular express middlewares

  • We’re importing the graphql schema. We didn’t define this yet, but we’ll be doing so soon

  • Whenever we’re running on development mode, we’re using the graphiQL middleware. This is a very useful web interface to interact with our GraphQL API, it shows the schema, available queries/mutations and has autocomplete capabilities

  • Finally we define the /api endpoint where our API will exist. There we use the graphqlExpress middleware and we pass the schema as an argument to it.

That’s it! As you can see, all we need to do to have a schema defined. The rest is quite simple. So now let’s see what that schema thing is and how to define one.

The GraphQL Schema

The first thing we’ll define is the entry point of the schema definition, the index.js file inside the src/graphql/schema directory. So create that file and paste this on it:

gist:xoor-io/2c231561251ac78ff3e05c7cb3c57611

This is what’s happening on this file:

  1. After importing the needed dependencies, we create the basic types we’ll be using to access the data. The Query type will be used to query data, and the Mutation type will be used to alter data. These will be the base building blocks for the rest of our schema, and will be extended by the rest of the elements on our schema.

  2. Within the Query type we define a query called status that will return a String.

  3. We then define the resolvers variable. Here is where we define how the queries/mutations will be handled by our app. Since we defined a status query inside our Query type, we need to define a resolver to handle that query and we make it return an “ok” string always.

  4. Then we need to collect all the type definitions on our schema. In this file we define just two: Query and Mutation. But we will have a few more (and you’ll have many more in a real application). So what we do is automate the type definition loading process using the node fs module to read the subdirectories within our current dir. We will assume that all the schema elements will be defined within folders and will have an index.js file that exports two elements: resolvers and types.

  5. Finally we build the schema using the makeExecutableSchema utility by passing the type defs and the resolvers.

Creating our Book schema

To make things clear, we’ll build the schema for a Book object and show how type defs and resolvers work with a specific example.

Let’s create a book folder inside our schema dir. Inside we’ll create 5 files:

  • _input.js: We’ll define here all the “input” types. Input types are used as arguments for Mutations.

  • _mutation.js: We’ll define here the mutations and the associated resolvers to each mutation type.

  • _query.js: Similar to mutations, but for queries.

  • _type.js: Here we’ll define our specific type, in this case the Book type, and any resolvers for the type (if any).

  • index.js will read from the previous 4 files and export the typedefs and resolvers.

Let’s see how these files boilerplate looks like:

_input.js:

gist:xoor-io/3a9e467db048d3e1c74ebf41cb59ddd2

_mutation.js:

gist:xoor-io/3eaf6524831d9784b01dfc2596048295

We’re not defining mutations on this example, but we leave you the boilerplate showing you how to define one. Same goes for inputs.

_query.js:

gist:xoor-io/181beb88ffc9f66fbac1dac928f143aa

We define a single query called books and define the resolver for it. In this case to keep things simple we return a static list of books, but in a real world application you’d connect to a database to retrieve that list.

_type.js:

gist:xoor-io/2a38db0fe7562a4351710ea2f630319d

Our Book type has two fields which are required: title and author.

index.js:

gist:xoor-io/c69dd03107f43ff1c86fb7715997cdc3

As explained before, we use the index.js file to import all the things we defined in the _xxxxx files and export them with a format that is known to our automated schema building process.

You probably noticed that we return types wrapped in a function that returns an array. This allows apollo to avoid duplicating types in the final big schema definition. More about this in the Schemas page of Apollo.

We are set to integrate apollo into our API. We have all the type definitions set, all we have to do now is “connect” Apollo to our express server.

Connecting Apollo to our Express server

We’ll head over to our server.js file and import both the graphql configuration for our app and the main config file.

// GraphQL - Apollo
import apollo from './graphql';

// Config
import config from './config';

Next we make sure that the app port is taken from the app config instead of being hardcoded:

gist:xoor-io/e037e8811401474c623346154cde1038

Next, after our status endpoint definition, with a single line of code we’ll integrate all the Apollo code into our express app by doing:

// Append apollo to our API
apollo(app);

In the end, our server.js file will look like this:

gist:xoor-io/e3604c48b65fe9023f59fbbd50965da3

Now you can run the project again and open up the GraphiQL interface by opening the http://localhost:5000/api/graphiql URL in your browser.

You can then run any queries or mutations in that pretty nice interface, as well as navigate the schema definition on the right side panel. So go ahead and use this as a query:

query {
  books {
    title
    author
  }
}

This should return the list of books and for each book it’s title and author:

And we’re done! We built a pretty simple GraphQL API using Node.js :) Now you have a basic knowledge on how to do it and you’re ready to move forward and start building your next app’s API using GraphQL to get all the advantages it has over RESTful APIs. It has some disadvantages too, for more info about the comparison you can surf the internet or read this nice post about it.

There are other interesting articles around the web, we found this one by Toptal's team a very good one to get a better understanding of GraphQL and Node.js working together.

Thanks for reading us again and if you liked it share the article on Twitter and follow us!