Midtype has shut down. The team is now working on Herald, helping product teams make customer-oriented decisions.

Why GraphQL is the Ideal API Language for Frontend Engineers

Facebook unveiled GraphQL to the developer community in 2015. What was once an internal research project quickly became the hot new standard for API development. Since then, it has grown tremendously, in terms of usage as well as mindshare—plenty of enterprises have already adopted it for some portion of their backend stack, including Github, PayPal, and Airbnb.

What is GraphQL?

GraphQL is a query language for APIs. It stands in comparison to REST, the most common standard for APIs built today. It allows clients to only fetch the data they need, and makes it easy to map relationships between data types (hence the “graph”).

Developing a GraphQL API starts with defining a schema that describes the data types your API will house, the relationships between those types, and the queries and mutations that allow you to perform common CRUD operations on your data. Consult the official documentation for the language to learn more about syntax and the various ways to implement your own GraphQL API.

Recent Trends in Frontend Engineering

Let’s first take a look at three of the biggest trends in frontend engineering over the last five years:

Better Tooling

The Javascript ecosystem has never been more robust. Since 2015, the number of npm modules has increased by 6x. While some of this growth stems from the rise of Node.js, a large chunk comes from the proliferation of frontend frameworks. Each of these frameworks comes with a number of tools to help with testing and developer experience.


A recent survey by npm reported roughly 61% of frontend engineers use Typescript, up by more than 31% from their previous survey. Previously, frontend engineers would discover most of their bugs while running their code in the browser. With static type-checking, many errors are caught much earlier, often even in the IDE while it’s being written.

Increased Client-Side Application Logic

Advancements in internet speed, browsers, and processors mean frontend clients can now perform many of the operations that were previously reserved for the backend.

GraphQL possesses features that enable advancements in each of these three areas. That has allowed it to thrive as the best API language for frontends. What was once difficult or impossible with REST APIs can be done in minutes with GraphQL. Let’s take a look at which specific GraphQL features allow for that advancement.

1. GraphQL’s Schema Introspection ⟶ Better Tooling

Every GraphQL API, regardless of the language or framework it’s built with, comes with documentation on every valid request that can be made to, as well as the response the client should expect. This enables developers to build robust tooling customized for the functionality of any GraphQL API.

The result is tools like GraphiQL and GraphQL-faker, two open-source projects that should be staples of any Frontend engineer’s toolkit.

GraphiQL allows you to test queries and generates API docs on the fly. The team at OneGraph has even built an explorer that allows you to interactively write and test queries and mutations.

GraphQL-Faker lets you stand up a mock API service that mimics your actual GraphQL API in minutes. Because introspection allows it to predict the exact shape of the response to any request, it can reliably “fake” them. Just update your API’s schema and GraphQL-faker can unblock frontend engineers while the new queries or mutations are implemented on the backend.

These are just two examples of the dozens of open-source tools that are only possible because of GraphQL’s introspection capabilities.

Why is this better than REST?

With REST APIs, the closest analog to GraphiQL is Swagger. But unless the API was built with Swagger’s tooling, it’s incredibly time consuming to write out the request parameters and response for every endpoint in the API. GraphiQL will work with any GraphQL API immediately. As for mocking data the way GraphQL-faker does, doing the same for a REST API requires writing out fake responses for every endpoint and continually maintaining them as your API evolves—this can be tedious and error prone.

2. GraphQL’s Typed Schema ⟶ Great Complement to Typescript

Typescript enables engineering teams to leverage Javascript’s speed of development with guardrails that also allow them to move safely.

GraphQL is the perfect API language to complement Typescript because it is itself strongly typed. That, along with the aforementioned introspection capabilities, means you can use tooling like Apollo Codegen to automatically validate the queries and mutations you’re making in your project and autogenerate Typescript interfaces that add types for the expected response.

Say, for example, you have a GraphQL schema that looks like this:

type Character {
  name: String!
  age: Int!

type Query {
  character(age: Int!): Character

And in your frontend you’ve defined the following query:

# src/queries/getCharacter.gql

query GetCharacter {
  character(age: 21) {

To automatically generate types for this query, install Apollo’s tooling package globally with npm install -g apollo and run the following command from the root of your project:

apollo codegen:generate --endpoint=https://[GRAPHQL_SERVER] \
	--target=typescript --includes=src/**/ --tagName=gql \
	--addTypename --outputFlat=src/graphql/types

This will generate a Typescript file at src/graphql/types/getCharacter.ts that looks like this:

// ====================================================
// GraphQL mutation operation: getCharacter
// ====================================================

export interface getCharacter_character {
  __typename: "Character";
  name: string;
  id: string;

export interface getCharacter {
  character: getCharacter_character | null;

If you’re using React to build a <CharacterBio> component that maps to a Character GraphQL type, you can use that interface in your React functional component like so (note that this is Typescript, not pure Javascript):

import { getCharacter_character } from "./gen/queries";

const CharacterBio: React.FC<getCharacter_character> = props => (
  <div className="character">
    <h3 className="character__name">{props.name}</h3>
    <span className="character__age">({props.age})</span>

Your frontend is constantly validating that the queries you intend to make are possible based on the schema of your API, and that you’re correctly using the response in your components. This is incredibly important as teams grow. It means you’re never left wondering whether the response from an API request will take the shape you expect.

Why is this better than REST?

This type of compile-time validation is impossible with REST APIs. Even if the API follows OpenAPI standards, there exists no tooling to integrate it this cleanly with Typescript. That means you’re left to catch errors only when they appear in your browser’s network console.

3. GraphQL’s Flexible Querying ⟶ Advanced Client-Side Application Logic

One of the traps of REST APIs lead to is designing a backend that aligns exactly with the views required on the frontend. This can be limiting because every time a new view is required, backend teams need to design and implement a new endpoint for the frontend to use.

For example, let’s say you’re building a project that needs to store a bunch of task objects. Each task has the following fields:

  • summary - text summary of the task.
  • completed - boolean describing whether or not it’s complete.
  • assignee - id of a single user object that the task is assigned to.

GraphQL gives frontend engineers the flexibility to fetch only the fields and the data they need to get the job done. It’s also designed to make fetching relational data simple. Frontend engineers can harness the querying power of a language like SQL in a syntax that feels like JSON.

With GraphQL, the schema for this app could look something like this:

type User {
  id: String!
  name: String!
  email: String!
  photo: String!

type Task {
  summary: String!
  completed: Boolean!
  assignee: User!

type Query {
  tasks(userId: Int): [Task!]!

This means that you immediately have the power to query tasks with any filter and fetch every task’s assignee, along with their name and photo. This can all be done in a single query:

# Fetch all tasks for user 1
query {
  tasks(userId: 1) {

# Fetch all tasks for all users, along with the assignee's name and photo.
query {
  tasks {
    assignee {

Because the syntax so closely aligns with JSON, it’s very easy for any frontend engineer to pick up. Pretty powerful stuff, right?

Why is this better than REST?

With a REST API, showing all the tasks for a given user is pretty easy. We just need a GET endpoint that filters based on assignee, so something like /api/tasks/:userId.

But what if now we wanted to show a stream of all tasks, along with the photos and names of the users they are assigned to. Well, right now our task object only returns the user’s id. We have two options:

  1. Augment the response to also include the user’s name and photo. These fields may not be used all the time, but will always be fetched.
  2. Stand up a second endpoint that returns all users, with their id, photo, and name, and then match the two up on the frontend.

In the GraphQL example above, you can accomplish both these tasks with the same query. When you want tasks across all users, omit the userId input argument. When you only want tasks for one user, include the userId, but don’t fetch the user’s name and photo for every task.

What Are GraphQL’s Shortcomings?

There are certainly tradeoffs to be considered when using GraphQL, especially for frontend engineers.

Debugging Can Be Difficult

One big difference between REST APIs and GraphQL APIs are that the latter has only one endpoint. All requests are sent to that one endpoint via POST, and so debugging the responses from an API on the frontend can get frustrating when you just see a bunch of requests like this:

graphql requests

GraphQL also doesn’t employ error codes the way REST APIs do. There is a wide set of predefined HTTP status codes that are designed to cover most issues the frontend might need to handle. That standard protocol makes it easier for a REST API to communicate an error to the frontend. GraphQL, on the other hand, provides very little direction in terms of error handling. The only requirement is that an error is returned with a message field that provides a string description of the issue. Frontend clients like Apollo help with parsing those errors, but because there is no communication protocol like with HTTP status codes, every application needs to design one that works for their needs.

Adds Complexity Tradeoff

While GraphQL can make complex queries relatively easy to write, it can also make simple queries a little more difficult to write and parse. Let’s say, for example, you wanted to retrieve a specific task with id 1 from your API and log it in the console. With REST, you could write the following fetch call in your frontend:

  .then(res => res.json())
  .then(task => console.log(task))
  .catch(e => throw Error(e));

Pretty straightforward. To do the same thing with GraphQL, you would need to make the following fetch call:

fetch("https://api.myapp.com/graphql", {
  method: "POST",
  body: JSON.stringify({
    query: `query GetTask($id: Int!) {
      task(id: $id) {
        assignee {
]     }
    variables: { id: 1 }
  .then(res => res.json())
  .then(res => console.log(res.data.task))
  .catch(e => throw Error(e));

That’s a fair bit more code for a relatively simple operation. While it may lead to more readable code, it also comes with the overhead of understanding how GraphQL POST requests must be structured.


Frontend frameworks are in a constant state of flux, but recent developments in the community have paved a path for GraphQL to serve as a great complement to the state-of-the-art frontend tech stack. As with any emerging technology, it comes with a non-trivial learning curve, so it may not always be the best choice. But speaking from experience, once you move to GraphQL, it’s tough to go back!