paint-brush
Building Your First GraphQL Serverby@nwthomas
4,588 reads
4,588 reads

Building Your First GraphQL Server

by nwthomasAugust 18th, 2019
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

This article is part of an ongoing series on technical and soft skills from Nathan Thomas, a full stack software engineer studying and working at Lambda School based in Silicon Valley. Getting started with GraphQL is a relatively new kid on the block — it's called GraphQL. At its core, it's a protocol disguised as a data-querying language. It is a layer that sits on top of your server and interfaces with both the server and the client. For now, let’s hop directly into the deep end and set up a server.

People Mentioned

Mention Thumbnail

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Building Your First GraphQL Server
nwthomas HackerNoon profile picture

(This article is part of an ongoing series on technical and soft skills from Nathan Thomas, a full stack software engineer studying and working at Lambda School based in Silicon Valley. Click here for the previous article in the series, a piece on “The Pursuit of Personal Fulfillment.”)

Getting Started with GraphQL

I don’t know if you’ve heard, but there’s a relatively new kid on the block — it's called GraphQL. 

It’s super fancy. 🎉

If you’re anything like me, you’ve built lots of RESTful servers. When you finally take a break to squash your existential dread and document 150 new endpoints, you might start wondering if there’s a better way to live your life.

On top of that, you might start to notice that your server often delivers too much information that’s unneeded for a given request. When the client makes a request from your server, it ends up getting the entire forest along with the single tree of data (I know, my examples need work). 

This is where GraphQL comes in. GraphQL is fast replacing RESTful APIs everywhere you might look, and more-and-more companies are claiming that it’s sped up their applications significantly.

As always, grab a cup of something good. ☕️

Strap in. This one’s crazy, but I think you’ll find it‘s worth it in the end.

It’s Time to Start Thinking in Edges

Graphs are all around us in life, and yet we never notice them. When you travel on public transit, discuss your family tree, or look at the stars and try to make out a bull or a large goat person shooting an arrow (like how I showed off my astronomy knowledge there?), we are unknowingly thinking in terms of graphs.

In computer science, graphs are connected points, and these points are traditionally called nodes or vertices. The lines that connect these graphs together are called edges. Here’s an example of a simple graph:

The graph above has five nodes (or vertices) and eight edges. Make sense, right?

Without getting into the nitty gritty of in-depth graph theory, we can access various nodes that are connected through edges by traveling along that edge to another node. In our data, this means that we connect different data tables together in a way that has significant meaning (this will make more sense later in the article).

This was one of the ways of thinking that allowed GraphQL to be born. Coupled with a general dissatisfaction for the current state of RESTful APIs in today’s data-heavy applications, Facebook decided to pioneer a new way to fetch data from their servers back in 2012 (and finally open-sourced it in 2015). By focusing on the data connections (instead of thinking in endpoints), they developed a new process for obtaining information from their servers.

GraphQL is, at its core, a protocol disguised as a data-querying language. It is a layer that sits on top of your server and interfaces with both the server and the client. Here’s an example of a query from the front-end that we could write later once we have the server built out:

query {
  getAllUsers {
    id
    username
    phone
  }
}

When we fire this off (which we’ll see how to do soon), we get the following JSON data back:


{
  "data": {
    "getAllUsers": [
      {
        "id": "1",
        "username": "admin",
        "phone": "(777) 777-7777"
      },
      {
        "id": "2",
        "username": "Chadrick54",
        "phone": "1-213-614-0707 x94837"
      },
      {
        "id": "3",
        "username": "Eva27",
        "phone": "396-178-0064"
      },
      {
        "id": "4",
        "username": "Audreanne_Luettgen65",
        "phone": "1-208-921-9879 x9843"
      },
      {
        "id": "5",
        "username": "Ahmed56",
        "phone": "(675) 410-7905 x6909"
      }
    ]
  }
}

Notice how the data directly parallels the query that we made? That’s super declarative code. We’ll talk more about that in a bit. For now, let’s hop directly into the deep end and set up a server. 🔥

“Whatever affects one directly, affects all indirectly.” — Martin Luther King Jr.

Setting Up the Server and Postgres Database

We’ll be starting from a mid-way point with our server this time around (but if you want to learn how to build the barebones of an Express.js server from scratch, check out my previous article here). 👍

Go ahead and clone this repository for the starter files. This will catch you up to exactly where you need to be to follow along with this walkthrough. Once you clone it and open it up in your editor of choice (I’ll be using VS Code), you should have something very much like this:

Make sure to use the command

yarn
to install all of your node modules as well. Once these have finished downloading, feel free to poke through the repository a bit and see what code is already finished for you.

We’ll be building out our API to allow us to work with users and their corresponding favorite candy. I’ve already built out a lot of the core functionality along with the migrations and dummy seed data files, and we’ll just be focusing on implementing GraphQL. 👍

Go ahead and run the command

yarn server
in the root directory. You should see something much like this:

However, our server isn’t fully wired up yet. Before we go any further, let’s head into our next section and hook up some good old-fashioned

PostgreSQL
.

“We are all now connected by the Internet, like neurons in a giant brain.” — Stephen Hawking

Setting Up PostgreSQL

We’ll be using Heroku today for our database (purely because it’s super easy to grab a full instance of

PostgreSQL
), although you’re free to use a Docker instance or something else if you want.

If you haven’t already done so in the past, create a Heroku account here and then click the "New" dropdown menu and "Create new app." If you see the screen below, you’ve gone the right direction:

Next, click on the "Resources" tab. It should look like this:

Type "postgres" into the “Add-ons” search input and select the “Heroku Postgres” option that appears. You’ll then be asked if you want to provision a free Hobby-tier server, and you should say “yes.”

After you do that, your screen should look like this:

Click on “Heroku Postgres” in purple letters, and this will take you to a new screen. On that screen, select “Settings” from the tabs and then click the “View Credentials” link shown below:

Grab the URI link listed in the resources. Here’s a fake link to demonstrate what it should look like:

postgres://ridsyuidtuuo:1f63063581eef3f1a6f09575e26781d0e81eed0a82b5e1705b518a54937a4853@ec2-50-19-221-37.compute-1.amazonaws.com:5132/d7nnugsfg4ljnk

Copy that link, and let’s head back over to our local server. Create a file called 

.env
in your root directory and paste the following in:

PORT=4000

DATABASE_URL=<your database URI link goes here>

This gives the boilerplate I made for you access to the PostgreSQL instance that you just set up, and we’re also assigning a custom port for your server.

With all of that out of the way, we can finally get to work on GraphQL. Go ahead and take a break to step outside. Grab some more coffee or tea. 

Ready?

Let’s go. 💪

GraphQL Structure

In a normal RESTful API, we have our typical

GET
,
POST
,
PUT
, and
DELETE
requests. These have been the gold standard for HTTP requests for years, and with good reason; they work, and they work well.

However, modern applications keep running into situations where developers have to build out an excessive number of endpoints. This can create a burden on the development process, and it certainly expands the amount of documentation that is required for the API.

In contrast, GraphQL simplifies everything down to one endpoint,

/graphql
. Open up the
server.js
file inside the api directory and look for the following code snippet:

server.use(
  '/graphql',
  graphqlHTTP({
    schema,
    graphiql: false
  })
);

This is the single endpoint, a gateway into our server. It will allow us to perform all of our queries and mutations (which replace our

GET
,
POST
,
PUT
, and
DELETE
requests). In order for our server to work, our next step needs to be to flesh out the schema that we use in the code snippet above. Notice that there’s already an import statement in our
server.js
file for it that looks like this:

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

Additionally, check out the

expressPlayground
middleware import that is also contained in our
server.js
file. The import statement looks like this:

const expressPlayground = require('graphql-playground-middleware-express').default;

Out of the box, GraphQL comes with

GraphiQL
, an IDE to practice server calls in your browser. I’ve replaced it with
GraphQL Playground
(and turned off
GraphiQL
with the
graphiql: false
statement in the snippet at the beginning of this section) as it is much more robust and better looking (which is obviously the most important quality in the development process 💅).

The configuration for this is also in the server.js file with the following code:

server.get('/playground', expressPlayground({ endpoint: '/graphql' }));

This allows us to use our new

GraphQL Playground
IDE. With your server running (which, just in case you shut it down, can be done with the command
yarn server
), got to
localhost:4000/playground
link in your browser. Your preferred browser will open up and you’ll be taken to a page that looks like this:

Doesn’t that dark mode in

GraphQL Playground
look amazing? If you’re seeing this, you’re in the right place. However, our server has a problem — We have an error (visible in the image above) that says that the server can’t be reached. It’s currently doing that because we still need to build out our queries, schemas, and migrate over our tables and data. 

Let’s flip over to the next section and get to work on our GraphQL server structure.

“When one tugs at a single thing in nature, he finds it attached to the rest of the world.” — John Muir

GraphQL Types

GraphQL is a strongly-typed querying language. This means that it validates the types of data that we are manipulating while using it. In order for this to work, we have to define

Types
 — These will correspond directly to the tables in our SQL database (although you are certainly free to use no-SQL databases like MongoDB with GraphQL), and correlate to the use of objects or structs in Object Oriented Programming with languages like Java, Golang, and C.

In order to define a type, you must use imports from the

graphql
dependency to define strings, integers, and other data types that you expect to receive through GraphQL for each field (username, email, etc.). Here’s an example of a
UserType
that we’ll be using in our server:

const graphql = require('graphql');

const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLNonNull
} = graphql;
  
const UserType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
    id: { type: GraphQLID },
    username: { type: new GraphQLNonNull(GraphQLString) },
    email: { type: new GraphQLNonNull(GraphQLString) },
    pictureUrl: { type: GraphQLString },
    street1: { type: GraphQLString },
    street2: { type: GraphQLString },
    city: { type: GraphQLString },
    state: { type: GraphQLString },
    zip: { type: GraphQLString },
    type: { type: GraphQLString },
    phone: { type: GraphQLString },
  })
});

module.exports = {
  UserType
};

Notice how we’re requiring the package

graphql
and then destructuring out our types from it (strings, ID, object, etc.). We can then define a schema, or group of fields, of what we expect to receive for a given object of data (in this case, our users). Finally, we’re exporting our
UserType
out to use later in our server.

Go ahead and make a file called

types.js
in the
schema
directory of the server in your server files. Look over the code below, and then put the following code into the file:

const graphql = require('graphql');

const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList
} = graphql;

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
    id: { type: GraphQLID },
    username: { type: new GraphQLNonNull(GraphQLString) },
    email: { type: new GraphQLNonNull(GraphQLString) },
    pictureUrl: { type: GraphQLString },
    street1: { type: GraphQLString },
    street2: { type: GraphQLString },
    city: { type: GraphQLString },
    state: { type: GraphQLString },
    zip: { type: GraphQLString },
    type: { type: GraphQLString },
    phone: { type: GraphQLString },
    candy: {
      type: new GraphQLList(CandyType),
      resolve(parent, args) {
        return Candy.findByUserId(parent.id);
      }
    }
  })
});

module.exports = {
  UserType
};

See that field called

candy
? That’s interesting, right?

Remember how we were talking earlier about

edges
and how they connect
nodes
, or
vertices
, together? Well, this is where we’re defining an
edge
that will connect us along our graph in our database to another
node
. Notice that we define the
candy
field as being a
GraphQLList
(which means it will essentially show up as an array of candy in our
JSON
) of
CandyType
. Furthermore, the resolver function returns a database query where we search
PostgreSQL
for the parent’s
id
property (essentially the user’s
id
from the fields above).

We’ll use this field later to access data about the

candy
table associated with each user in the
user
table.

However, we have a bit of a problem — we don’t have a

CandyType
that this field seems to be referencing. For time’s sake, I’m going to give you this code to copy-and-paste into your code right below the
UserType
. Here’s the completed file code for you:

const graphql = require('graphql');
const User = require('../models/userModel.js');
const Candy = require('../models/candyModel.js');

const {
  GraphQLObjectType,
  GraphQLID,
  GraphQLString,
  GraphQLNonNull,
  GraphQLList
} = graphql;

const UserType = new GraphQLObjectType({
  name: 'User',
  fields: () => ({
    id: { type: GraphQLID },
    username: { type: new GraphQLNonNull(GraphQLString) },
    email: { type: new GraphQLNonNull(GraphQLString) },
    pictureUrl: { type: GraphQLString },
    street1: { type: GraphQLString },
    street2: { type: GraphQLString },
    city: { type: GraphQLString },
    state: { type: GraphQLString },
    zip: { type: GraphQLString },
    type: { type: GraphQLString },
    phone: { type: GraphQLString },
    candy: {
      type: new GraphQLList(CandyType),
      resolve(parent, args) {
        return Candy.findByUserId(parent.id);
      }
    }
  })
});

const CandyType = new GraphQLObjectType({
  name: 'Candy',
  fields: () => ({
    id: { type: GraphQLID },
    candyName: { type: new GraphQLNonNull(GraphQLString) },
    userId: { type: new GraphQLNonNull(GraphQLID) },
    user: {
      type: UserType,
      resolve(parent, args) {
        return User.findById(parent.userId);
      }
    }
  })
});

module.exports = {
  UserType,
  CandyType
};

We’ve now completed the circle. We have a

CandyType
that will pull information from our database (and even has the ability to query the user associated with each candy in the
user
field above) but is also connected to its corresponding user (in a one-to-many relationship from the
user
table to the
candy
table).

We’re about to see how useful all of this is. The next sections will be crazy, but they’ll go really fast. We’ll have your server finished in no time. 🔥 🚒

GraphQL Queries

Queries are analogous to

GET
requests on a RESTful API, but the way in which we can write them in GraphQL (which we saw at the beginning of this article) makes them really unique and extremely declarative.

In software engineering, declarative design (in case you don’t want to spend the next 10 minutes waist-deep in that Wikipedia link) is any process that allows you to write your code in a way that describes what it does instead of how it does it.

Create a new file in your

schema
directory called
query.js
, and paste the following code into it. It’s a lot, but there’s a ton of repetition (as you’ll see soon) that makes it ultimately easy to learn:

const graphql = require('graphql');
const Candy = require('../models/candyModel.js');
const User = require('../models/userModel.js');
const { UserType, CandyType } = require('./types.js');

const { GraphQLObjectType, GraphQLList, GraphQLID, GraphQLNonNull } = graphql;

const RootQuery = new GraphQLObjectType({
  name: 'RootQueryType',
  fields: {
    getAllUsers: {
      type: new GraphQLList(UserType),
      description: 'Gets all users',
      resolve() {
        return User.find()
          .then(res => {
            if (res.length) {
              return res;
            }
            return new Error('The users could not be found.');
          })
          .catch(() => {
            return new Error('There was an error completing your request.');
          });
      }
    },
    getUserById: {
      type: UserType,
      description: 'Gets a user by user ID',
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(parent, args) {
        return User.findById(args.id)
          .then(res => {
            if (res) {
              return res;
            }
            return new Error('The user could not be found.');
          })
          .catch(() => {
            return new Error('There was an error completing your request.');
          });
      }
    },
    getAllCandy: {
      type: new GraphQLList(CandyType),
      description: 'Gets all candy',
      resolve() {
        return Candy.find()
          .then(res => {
            if (res.length) {
              return res;
            }
            return new Error('The candy could not be found.');
          })
          .catch(() => {
            return new Error('There was an error completing your request.');
          });
      }
    },
    getCandyById: {
      type: CandyType,
      description: 'Gets a candy by candy ID',
      args: { id: { type: new GraphQLNonNull(GraphQLID) } },
      resolve(parent, args) {
        return Candy.findById(args.id)
          .then(res => {
            if (res) {
              return res;
            }
            return new Error('The candy could not be found.');
          })
          .catch(err => {
            return new Error('There was an error completing your request.');
          });
      }
    }
  }
});

module.exports = RootQuery;

Don’t worry. Like I said, it’s a lot of repetition. Let’s take the

getCandyById
query at the end of the file above and dissect it (since it’s close to us right now and is a great example of the rest of the queries). 

First, we define what “type” our query is supposed to return. We just coded those up in the last section, and we imported them at the beginning of this

query.js
file.

Second, we have a description of the query. This will pop up in GraphQL Playground. It’s not necessary, but it’s a really nice touch for other developers to read in

GraphQL Playground
later when they’re testing out writing queries for our server.

Next, we have our arguments, or

args
. These are what we expect our server to receive when someone makes this
getCandyById
query. In this instance, we have an
id
argument of type
GraphQLID
. This is a unique import from the
graphql
package that allows the argument sent to our server to be either a number or a string (i.e. 1 or “1”).

Additionally, we have a

resolve
function. This takes in
parent
properties (which we aren’t using here) as the first argument and
args
(or arguments, our
id
in this case) as the second one. We then proceed to query our
PostgreSQL
database and return the response in our promise. If the data cannot be found, we return a new error with a message.

Finally, navigate over to your

schema.js
file, and update it to look like this:

const graphql = require('graphql');
const { GraphQLSchema } = graphql;
const RootQuery = require('./query.js');

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: null
});

We just imported the queries from query.js and placed it into our schema setup. The only thing we still need to do in order to use our server is to roll out some mutations. 

Hold onto your butts. 👍

“Invisible threads are the strongest ties.” — Friedrich Nietzsche

GraphQL Mutations

The final puzzle piece for our server is to upgrade our code with “mutations.” These are the GraphQL equivalents of

POST
,
PUT
, and
DELETE
requests in a RESTful API.

Create a new file called

mutation.js
and paste the following code in:

const graphql = require('graphql');
const User = require('../models/userModel.js');
const { UserType } = require('./types.js');

const { GraphQLString, GraphQLNonNull, GraphQLID, GraphQLObjectType } = graphql;

const Mutation = new GraphQLObjectType({
  name: 'Mutation',
  fields: () => ({
    addUser: {
      type: UserType,
      args: {
        username: { type: new GraphQLNonNull(GraphQLString) },
        email: { type: new GraphQLNonNull(GraphQLString) },
        pictureUrl: { type: GraphQLString },
        street1: { type: GraphQLString },
        street2: { type: GraphQLString },
        city: { type: GraphQLString },
        state: { type: GraphQLString },
        zip: { type: GraphQLString },
        type: { type: GraphQLString },
        phone: { type: GraphQLString }
      },
      resolve(parent, args) {
        return User.insert(args)
          .then(res => {
            if (res) {
              return res;
            }
            return new Error('The new user could not be created.');
          })
          .catch(() => {
            return new Error('There was an error completing your request.');
          });
      }
    },
    updateUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLID) },
        username: { type: new GraphQLNonNull(GraphQLString) },
        email: { type: new GraphQLNonNull(GraphQLString) },
        pictureUrl: { type: GraphQLString },
        street1: { type: GraphQLString },
        street2: { type: GraphQLString },
        city: { type: GraphQLString },
        state: { type: GraphQLString },
        zip: { type: GraphQLString },
        type: { type: GraphQLString },
        phone: { type: GraphQLString }
      },
      resolve(parent, args) {
        return User.update(args.id, args)
          .then(res => {
            if (res) {
              return res;
            }
            return new Error('The user could not be updated.');
          })
          .catch(() => {
            return new Error('There was an error completing your request.');
          });
      }
    },
    deleteUser: {
      type: UserType,
      args: {
        id: { type: new GraphQLNonNull(GraphQLID) }
      },
      resolve(parent, args) {
        return User.remove(args.id);
      }
    }
  })
});

module.exports = Mutation;

Once again, I know this looks like a lot, but it’s ultimately just a lot of repetition. Let’s look at the

deleteUser
mutation here at the end of the file as an example.

Notice that we’ve once again defined our type,

UserType
. In addition, we’re expecting an argument of
id
. Finally, we are using a resolver function to ping our
PostgreSQL
database and remove the row of user data where the
args.id
matches the user.

Look over the other mutations using this knowledge and see if you can figure out what everything else is doing. There’s nothing complicated here; it’s all just more of the same from what you’ve already seen. When you're done, update the

schema.js
file one more time to import our mutations and look like this:

const graphql = require('graphql');
const { GraphQLSchema } = graphql;
const RootQuery = require('./query.js');
const Mutation = require('./mutation.js');

module.exports = new GraphQLSchema({
  query: RootQuery,
  mutation: Mutation
});

Using GraphQL Playground

It’s time to enjoy using our GraphQL server. 🍾 🎉 🎊 🙌

First, let’s migrate our data tables and seeds on

PostgreSQL
by running the following commands in your terminal:

  1. npx knex migrate:latest
  1. npx knex seed:run

Once you’ve finished, run the command

yarn server
in your terminal (if you’re not already running your server) and click on the
localhost:4000/playground
link.

Then, write the following query in the lefthand side of the screen and press the “play” button:

query {
  getAllUsers {
    id
    username
    email
    street1
    city
    state
    zip
    type
    phone
    candy {
      id
      candyName
    }
  }
}

This queries our server for all users and returns the fields above that we requested. Your

GraphQL Playground
instance should look like this:

Finally, let’s do a quick mutation. Open a new tab in

GraphQL Playground
, and paste the following mutation into the lefthand side of the screen before pressing the “play” button:

mutation {
  addUser (
    username: "martymcfly"
    email: "[email protected]"
    street1: "9303 Lyon Drive"
    city: "Hill Valley"
    state: "CA"
    zip: "95420"
    phone: "(777) 777-7777"
  ) {
    id
    username
    email
    street1
    city
    state
    zip
    type
    phone
  }
}

This should create a new user and return data as we’ve specified in the return fields we expect above. Your browser window should look like the following:

See if you can write other mutations and queries out. You can check out the various ones available to you (along with the types we created on the server) by clicking on the

DOCS
and
SCHEMA
tabs on the righthand side of the page:

Conclusion

Now that we’ve been through a full tour of duty with GraphQL, hopefully you can see why people enjoy using it. It’s simple (once you get it set up), and it’s honestly a lot of fun.

See if you can go back to the

mutations.js
file and make all of the mutations for the
CandyType
, including a
createNewCandy
,
updateCandyById
, and
deleteCandyById
.

Finally, as you continue to build out APIs, GraphQL is a great choice; pairing it with

Express.js
makes middleware implementation easy, and our PostgreSQL setup means you can now scale this framework with one of the most popular databases out there.

If you end up building something awesome, feel free to tweet me with it. I’d love to see what you can create. 🏗

Additional Resources for Your Journey

  1. GraphQL Documentation — The official documentation on GraphQL is excellent and a great way to get started with customizing this server for your own application
  2. Apollo-Boost — Apollo-Boost is a quick and easy way to get started writing front-end mutations and queries for your application with GraphQL
  3. Resolver Best Practices — A brilliant article on best practices while writing your resolver functions in GraphQL


Thanks for reading. 🔥

Nathan

(GitHub, LinkedIn, Twitter, and Portfolio Site)