Building a GraphQL API in JavaScript
Tags: graphql, api, javascript
Published: 2020-06-24 19:41:42

---
title: Building a GraphQL API in JavaScript
published: true
date: 2020-06-24 15:30:00 UTC
tags: graphql, api, javascript
canonical_url: https://blog.heroku.com/building-graphql-api-javascript
---

Over the last few years, [GraphQL](https://graphql.org/) has emerged as a very popular API specification that focuses on making data fetching easier for clients, whether the clients are a front-end or a third-party.

In a traditional REST-based API approach, the client makes a request, and the server dictates the response:

```bash
$ curl https://api.heroku.space/users/1

{
  "id": 1,
  "name": "Luke",
  "email": "luke@heroku.space",
  "addresses": [
    {
      "street": "1234 Rodeo Drive",
      "city": "Los Angeles",
      "country": "USA"
    }
  ]
}

```

But, in GraphQL, the client determines precisely the data it wants from the server. For example, the client may want only the user's name and email, and none of the address information:

```bash
$ curl -X POST https://api.heroku.space/graphql -d '
query {
  user(id: 1) {
    name
    email
  }
}
'

{
  "data":
    {
      "name": "Luke",
      "email": "luke@heroku.space"
    }
}

```

With this new paradigm, clients can make more efficient queries to a server by trimming down the response to meet their needs. For single-page apps (SPAs) or other front-end heavy client-side applications, this speeds up rendering time by reducing the payload size. However, as with any framework or language, GraphQL has its trade-offs. In this post, we'll take a look at some of the pros and cons of using GraphQL as a query language for APIs, as well as how to get started building an implementation.

## [Why would you choose GraphQL?](#why-would-you-choose-graphql)

As with any technical decision, it's important to understand what advantages GraphQL offers to your project, rather than simply choosing it because it's a buzzword.

Consider a SaaS application that uses an API to connect to a remote database; you'd like to render a user's profile page. You might need to make one API `GET` call to fetch information about the user, like their name or email. You might then need to make another API call to fetch information about the address, which is stored in a different table. As the application evolves, because of the way it's architected, you might need to continue to make more API calls to different locations. While each of these API calls can be done asynchronously, you must also handle their responses, whether there's an error, a network timeout, or even pausing the page render until all the data is received. As noted above, the payloads from these responses might be more than necessary to render your current pages. And each API call has network latency and the total latencies added up can be substantial.

With GraphQL, instead of making several API calls, like `GET /user/:id` and `GET /user/:id/addresses`, you make one API call and submit your query to a single endpoint:

```graphql
query {
  user(id: 1) {
    name
    email
    addresses {
      street
      city
      country
    }
  }
}

```

GraphQL, then, gives you just one endpoint to query for all the domain logic that you need. If your application grows, and you find yourself adding more data stores to your architecture—PostgreSQL might be a good place to store user information, while Redis might be good for other kinds—a single call to a GraphQL endpoint will resolve all of these disparate locations and respond to a client with the data they requested.

If you're unsure of the needs of your application and how data will be stored in the future, GraphQL can prove useful here, too. To modify a query, you'd only need to add the name of the field you want:

```diff
    addresses {
      street
+     apartmentNumber # new information
      city
      country
    }

```

This vastly simplifies the process of evolving your application over time.

## [Defining a GraphQL schema](#defining-a-graphql-schema)

There are GraphQL server implementations in a variety of programming languages, but before you get started, you'll need to identify the objects in your business domain, as with any API. Just as a REST API might use something like [JSON schema](https://json-schema.org/), GraphQL defines its schema using SDL, or [Schema Definition Language](https://graphql.org/learn/schema/), an idempotent way to describe all the objects and fields available by your GraphQL API. The general format for an SDL entry looks like this:

```graphql
type $OBJECT_TYPE {
  $FIELD_NAME($ARGUMENTS): $FIELD_TYPE
}

```

Let's build on our earlier example by defining what entries for the user and address might look like:

```graphql
type User {
  name: String
  email: String
  addresses: [Address]
}

type Address {
  street: String
  city: String
  country: String
}

```

`User` defines two `String` fields called `name` and `email`. It also includes a field called `addresses`, which is an array of `Address` objects. `Address` also defines a few fields of its own. (By the way, there's more to a GraphQL [schema](https://graphql.org/learn/schema/) than just objects, fields, and scalar types. You can also incorporate interfaces, unions, and arguments, to build more complex models, but we won’t cover those for this post.)

There's one more type we need to define, which is the entry point to our GraphQL API. You'll remember that earlier, we said a GraphQL query looked like this:

```graphql
query {
  user(id: 1) {
    name
    email
  }
}

```

That `query` field belongs to a special reserved type called `Query`. This specifies the main entry point to fetching objects. (There’s also a `Mutation` type for modifying objects.) Here, we define a `user` field, which returns a `User` object, so our schema needs to define this too:

```graphql
type Query {
  user(id: Int!): User
}

type User { ... }
type Address { ... }

```

Arguments on a field are a comma-separated list, which takes the form of `$NAME: $TYPE`. The `!` is GraphQL's way of denoting that the argument is required—omitting means it's optional.

Depending on your language of choice, the process of incorporating this schema into your server varies, but in general, consuming this information as a string is enough. Node.js has [the `graphql` package](https://www.npmjs.com/package/graphql) to prepare a GraphQL schema, but we're going to use [the `graphql-tools` package](https://www.npmjs.com/package/graphql-tools) instead, because it provides a few more niceties. Let's import the package and read our type definitions in preparation for future development:

```javascript
const fs = require('fs')
const { makeExecutableSchema } = require("graphql-tools");

let typeDefs = fs.readFileSync("schema.graphql", {
  encoding: "utf8",
  flag: "r",
});

```

## [Setting up resolvers](#setting-up-resolvers)

A schema sets up the ways in which queries can be constructed but establishing a schema to define your data model is just one part of the GraphQL specification. The other portion deals with actually fetching the data. This is done through the use of [_resolvers_](https://graphql.org/learn/execution/#root-fields-resolvers). A resolver is a function that returns a field's underlying value.

Let's take a look at how you might implement resolvers in Node.js. The intent is to solidify concepts around how resolvers operate in conjunction with schemas, so we won't go into too much detail around how the data stores are set up. In the "real world", we might establish a database connection with something like [knex](https://knexjs.org/). For now, let's just set up some dummy data:

```javascript
const users = {
  1: {
    name: "Luke",
    email: "luke@heroku.space",
    addresses: [
      {
        street: "1234 Rodeo Drive",
        city: "Los Angeles",
        country: "USA",
      },
    ],
  },
  2: {
    name: "Jane",
    email: "jane@heroku.space",
    addresses: [
      {
        street: "1234 Lincoln Place",
        city: "Brooklyn",
        country: "USA",
      },
    ],
  },
};

```

GraphQL resolvers in Node.js amount to an Object with the key as the name of the field to be retrieved, and the value being a function that returns the data. Let's start with a barebones example of the initial `user` lookup by id:

```javascript
const resolvers = {
  Query: {
    user: function (parent, { id }) {
      // user lookup logic
    },
  },
}

```

This resolver takes two arguments: an object representing the parent (which in the initial root query is often unused), and a JSON object containing the arguments passed to your field. Not every field will have arguments, but in this case, we will, because we need to retrieve our user by their ID. The rest of the function is straightforward:

```javascript
const resolvers = {
  Query: {
    user: function (_, { id }) {
      return users[id];
    },
  }
}

```

You'll notice that we didn't explicitly define a resolver for `User` or `Addresses`. The `graphql-tools` package is intelligent enough to automatically map these for us. We can override these if we choose, but with our type definitions and resolvers now defined, we can build our complete schema:

```javascript
const schema = makeExecutableSchema({ typeDefs, resolvers });

```

## [Running the server](#running-the-server)

Finally, let's get this demo running! Since we're using Express, we can use [the `express-graphql` package](https://www.npmjs.com/package/express-graphql) to expose our schema as an endpoint. The package requires two arguments: your schema, and your root value. It takes one optional argument, `graphiql`, which we'll talk about in a bit.

Set up your Express server on your favorite port with the GraphQL middleware like this:

```javascript
const express = require("express");
const express_graphql = require("express-graphql");

const app = express();
app.use(
  "/graphql",
  express_graphql({
    schema: schema,
    graphiql: true,
  })
);
app.listen(5000, () => console.log("Express is now live at localhost:5000"));

```

Navigate your browser to `http://localhost:5000/graphql`, and you should see a sort of IDE interface. On the left pane, you can enter any valid GraphQL query you like, and on your right you'll get the results. This is what `graphiql: true` provides: a convenient way of testing out your queries. You probably wouldn't want to expose this in a production environment, but it makes testing much easier.

Try entering the query we demonstrated above:

```graphql
query {
  user(id: 1) {
    name
    email
  }
}

```

To explore GraphQL's typing capabilities, try passing in a string instead of an integer for the ID argument:

```graphql
# this doesn't work
query {
  user(id: "1") {
    name
    email
  }
}

```

You can even try requesting fields that don't exist:

```graphql
# this doesn't work
query {
  user(id: 1) {
    name
    zodiac
  }
}

```

With just a few clear lines of code expressed by the schema, a strongly-typed contract between the client and server is established. This protects your services from receiving bogus data and expresses errors clearly to the requester.

## [Performance considerations](#performance-considerations)

For as much as GraphQL takes care of for you, it doesn't solve every problem inherent in building APIs. In particular, caching and authorization are just two areas that require some forethought to prevent performance issues. The GraphQL spec does not provide any guidance for implementing either of these, which means that the responsibility for building them falls onto you.

### [Caching](#caching)

REST-based APIs don't need to be overly concerned when it comes to caching, because they can build on [existing HTTP header strategies](https://restfulapi.net/caching/) that the rest of the web uses. GraphQL doesn't come with these caching mechanisms, which can place undue processing burden on your servers for repeated requests. Consider the following two queries:

```graphql
query {
  user(id: 1) {
    name
  }
}

query {
  user(id: 1) {
    email
  }
}

```

Without some sort of caching in place, this would result in two database queries to fetch the `User` with an ID of `1`, just to retrieve two different columns. In fact, since GraphQL also allows for [aliases](https://graphql.org/learn/queries/#aliases), the following query is valid and also performs two lookups:

```graphql
query {
  one: user(id: 1) {
    name
  }
  two: user(id: 2) {
    name
  }
}

```

This second example exposes the problem of how to batch queries. In order to be fast and efficient, we want GraphQL to access the same database rows with as few roundtrips as possible.

[The `dataloader` package](https://github.com/graphql/dataloader) was designed to handle both of these issues. Given an array of IDs, we will fetch all of those at once from the database; as well, subsequent calls to the same ID will fetch the item from the cache. To build this out using `dataloader`, we need two things. First, we need a function to load all of the requested objects. In our sample, that looks something like this:

```javascript
const DataLoader = require('dataloader');
const batchGetUserById = async (ids) => {
   // in real life, this would be a DB call
  return ids.map(id => users[id]);
};
// userLoader is now our "batch loading function"
const userLoader = new DataLoader(batchGetUserById);

```

This takes care of the issue with batching. To load the data, and work with the cache, we'll replace our previous data lookup with a call to the `load` method and pass in our user ID:

```javascript
const resolvers = {
  Query: {
    user: function (_, { id }) {
      return userLoader.load(id);
    },
  },
}

```

### [Authorization](#authorization)

Authorization is an entirely different problem with GraphQL. In a nutshell, it's the process of identifying whether a given user has permission to see some data. We can imagine scenarios where an authenticated user can execute queries to get their own address information, but they should not be able to get the addresses of other users.

To handle this, we need to modify our resolver functions. In addition to a field's arguments, a resolver also has access to its parent, as well as a special _context_ value passed in, which can provide information about the currently authenticated user. Since we know that `addresses` is a sensitive field, we need to change our code such that a call to users doesn't just return a list of addresses, but actually, calls out to some business logic to validate the request:

```javascript
const getAddresses = function(currUser, user) {
  if (currUser.id == user.id) {
    return user.addresses
  }

  return [];
}

const resolvers = {
  Query: {
    user: function (_, { id }) {
      return users[id];
    },
  },
  User: {
    addresses: function (parentObj, {}, context) {
      return getAddresses(context.currUser, parentObj);
    },
  },
};

```

Again, we don't need to explicitly define a resolver for each `User` field—only the one which we want to modify.

By default, `express-graphql` passes the current HTTP `request` as a value for `context`, but this can be changed when setting up your server:

```javascript
app.use(
  "/graphql",
  express_graphql({
    schema: schema,
    graphiql: true,
    context: {
    currUser: user // currently authenticated user
    }
  })
);

```

## [Schema best practices](#schema-best-practices)

One aspect missing from the GraphQL spec is the lack of guidance on versioning schemas. As applications grow and change over time, so too will their APIs, and it's likely that GraphQL fields and objects will need to be removed or modified. But this downside can also be positive: by designing your GraphQL schema carefully, you can avoid pitfalls apparent in easier to implement (and easier to break) REST endpoints, such as inconsistencies in naming and confusing relationships. Marc-Andre has [listed several strategies](https://www.apollographql.com/blog/graphql-schema-design-building-evolvable-schemas-1501f3c59ed5) for building evolvable schemas which we highly recommend reading through.

In addition, you should try to keep as much of [your business logic separate from your resolver logic](https://graphql.org/learn/thinking-in-graphs/#business-logic-layer). Your business logic should be a single source of truth for your entire application. It can be tempting to perform validation checks within a resolver, but as your schema grows, it will become an untenable strategy.

## [When is GraphQL not a good fit?](#when-is-graphql-not-a-good-fit)

GraphQL doesn't mold precisely to the needs of HTTP communication the same way that REST does. For example, GraphQL specifies only a single status code—`200 OK`—regardless of the query’s success. A special `errors` key is returned in this response for clients to parse and identify what went wrong. Because of this, error handling can be a bit trickier.

As well, GraphQL is just a specification, and it won't automatically solve every problem your application faces. Performance issues won't disappear, database queries won't become faster, and in general, you'll need to rethink everything about your API: authorization, logging, monitoring, caching. Versioning your GraphQL API can also be a challenge, as the official spec currently has no support for handling breaking changes, an inevitable part of building any software. If you're interested in exploring GraphQL, you will need to dedicate some time to learning how to best integrate it with your needs.

## [Learning more](#learning-more)

The community has rallied around this new paradigm and come up with [a list of awesome GraphQL resources](https://github.com/chentsulin/awesome-graphql), for both frontend and backend engineers. You can also see what queries and types look like by [making real requests on the official playground](https://graphql.org/swapi-graphql/).

We also have a [Code[ish] podcast episode](https://www.heroku.com/podcasts/codeish/44-graphqls-benefits-and-costs) dedicated entirely to the benefits and costs of GraphQL.