Learning GraphQL Roadmap: 8 Steps to Master GraphQL

The adoption of GraphQL is growing like crazy. And I love it! But the learning curve is steep and getting started can be overwhelming. In fact, there are many misconceptions about GraphQL - like people believing it’s an SQL alternative to talk directly with the database... GraphQL is a query language that rethinks the way a client interacts with the server. So you can think of it as a layer between a server and a client, not a query language for your database.

The number one advantage of GraphQL APIs is the ability to get only what you ask for. In reality it means 2 things:

  1. you get less unnecessary data (maybe you just want ids of your users, not all their information),
  2. you can combine several queries in a single request (eg. get all your users and their comments in a single query instead of two).

The goal of this post is not to explain all the concepts, but to give you a list of steps and resources you can go through to step up your GraphQL skills.

Here are the 8 steps to go from newcomer to shipping production-ready GraphQL APIs 👇

Step 1 - Schema Definition Language

GraphQL introduced a query language - which lead people to think it was a SQL alternative. This new language let backend developers describe their data and clients to write efficient queries to ask for exactly the data they need.

Therefore this language is used in 2 places:

  • the schema files describing the data - this acts as a contract between the backend and the frontend developers;
  • the HTTP requests from the client to query data.
# server - describing resources
type User {
	id: ID!
	username: String!
	email: String!
	comments: [Comment]
}

type Comment {
	id: ID!
	text: String!
	author: User!
	createdAt: Date!
	replyTo: Comment
}
# client - querying resources
query UserWithComments($id: ID!) {
  user(id: $id) {
  	username
        comments {
            id
            text
            createdAt
		}
	}
}

The second big point for GraphQL is that it’s a strongly-typed language. That means easier to maintain, test and debug, fewer bugs, and better (automated) documentation.

The language comes with the usual built-in types - Int, Float, String, Boolean - along with some modifiers to specify if the field is required (!) or a list ([String]) - (checkout this cool cheatsheet for more details).

On the client side, 3 operations are supported:

  1. queries allow you to ask for resources (like GET requests),
  2. mutations allow you to modify resources (like POST, PUT, PATCH and DELETE requests),
  3. subscriptions allow you to establish a 2-sided connection for real-time interactions (with websockets).

Step 2 - GraphQL servers

As we said above, GraphQL is an abstraction layer that stands between the incoming HTTP requests and the controllers.

This layer is responsible for translating the query, calling the corresponding resolvers to get the requested resources, and formatting the output following the same GraphQL structure.

Happily, we have some libraries to take care of all that heavy lifting for us! They are called GraphQL engines or servers. These libraries are frameworks that let you create a GraphQL API in just a few lines of code - here’s an example with Apollo in NodeJS:

const { ApolloServer, gql } import 'apollo-server'

const typeDefs = gql`
  type Query {
    hello: String
  }
`
const resolvers = {
  Query: {
    hello: () => {
        return 'Hello World!';
    }
  }
};

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

server.listen(4000)

Checkout this official page for a comprehensive list of servers for every language

Step 3 - GraphQL Clients

We’ve covered how to handle incoming requests. How about sending requests to the API?

This should be the first step really because GraphQL has been designed with consumers of the API in mind. It makes the life of frontend developers easier:

  • don’t bother finding the right endpoint, there’s only one;
  • just ask what you want and we’ll figure out how to mix, match and filter to give you just what you need.

What’s cool about it, is that you don’t have to draw complex libraries to manage requests. You can. But you don’t have to.

Here’s what the first request above looks like with fetch - the built-in browser API to send HTTP requests:

// app.js
const query = `query UserWithComments($id: ID!) {
	user(id: $id) {
		username
		comments {
			id
			text
			createdAt
		}
	}
}`
const variables = { id: 0 }
fetch('<https://example.com/api/graphql>',
	method: 'POST',
	headers: {'Content-Type': 'application/json'}
	body: JSON.stringify({query, variables})
)

Checkout the official website for a comprehensive list of clients

Step 4 - Extending the language with Fragments, Unions and Directives

My favourite part about GraphQL is how flexible it is! The language is simple enough to get you started but offers some powerful features like Fragments, Unions and Directives to up your game 🆙

These features help you set up complex access control rules, fully-typed error handling, and much more. Here are some quick videos about each of these topics:

Talking about error handling...

Step 5 - Error Handling

This is the annoying part, right? Well, it doesn’t have to be that way!

GraphQL errors have a clear structure defined in the spec and let you define your own custom errors. In development mode, you can easily enable the stack trace to see exactly where your errors come from.

https://spec.graphql.org/October2021/#example-8b658

Did you know that errors can be a huge security vulnerability? Checkout our previous post about handling errors from a security point of view

Do you know how to avoid breaking errors in production? Testing your code!

Step 6 - Testing

You didn’t think you could skip that part, did you?

Testing is an essential part of building reliable and scalable APIs. And because of the structural difference between REST and GraphQL, strategies for testing your APIs are going to be different.

describe('Queries', () => {
			let tester
			beforeAll(() => {
			    tester = new EasyGraphQLTester(schemaCode)
			})
			test('Should get friends with a nested query', () => {
	        const query = `
	            query GET_FRIENDS($userId: ID!){
	                user(id: $userId) {
	                    id
	                    friends {
	                        id
	                    }
	                }
	            }
	        `
	        tester.test(true, query, { userId: '0' })
	    })
})

Checkout our previous post to learn how to test your GraphQL API, from unit tests to integration tests

The TLDR is:

  • you need to test every layer of your API: schema definition, operations (queries and mutations) and resolvers;
  • some great libraries help you isolate these different parts: linters help you test your type definitions and EasyGraphQL lets you test and execute queries without a running server;
  • resolvers are just functions so you can test them as usual.

Step 7 - Security

Yet another often neglected part of building product-ready APIs: security.

But it’s even more relevant for GraphQL!

Why? every GraphQL server library is vulnerable by default!

If you don’t take the time to secure your API, it’s dead simple to break it or worse, leak your users’ data.

Here’s another TLDR for you:

  1. disable introspection and the GraphQL playground,
  2. limit access control with authentication and authorization,
  3. set a request timeout,
  4. limit the rate of incoming requests,
  5. limit the depth of queries,
  6. limit requested resources with a cost analysis,
  7. handle errors,
  8. validate inputs,
  9. use GraphQL over POST not GET requests.

Guess what? The community is fantastic and has built some amazing libraries and plugins to implement these steps in just a few lines of code!

Learn more in our previous post - 9 GraphQL Security Best Practices - and scan your endpoint at graphql.security. It’s free. No account required. (we built it 😇)

Step 8 - Caching

If you want to scale your API and make it blazingly fast you’re gonna need to set up caching.

No, GraphQL doesn’t “break caching”! There are tons of solutions out there.

But first, we need to know what we’re talking about. There are two sides to the equation: client-side caching and server-side caching.

For client-side caching, you can use GraphQL client libraries like Apollo client which also handles caching.

For server-side caching, you have neat solutions like GraphCDN or plugins (eg. the envelop plugin with GraphQL Yoga)

Other resources

GraphQL is moving fast. New tools, libraries and features pop up every week! Mastering the fundamental concepts is great, but keeping up to date will make you an expert and avoid some bad practices regarding performance, security, and maintainability.

Here are my favourite sources to learn and stay up to date:

  1. GraphQL Weekly - the weekly newsletter of everything GraphQL. You get the top 3 posts of the week, 1 OSS news, 1 video and 1 event in your inbox every week.
  2. Jamie’s short 5-min videos on very specific GraphQL topics.
  3. The GraphQL subreddit and discord group to ask questions and stay up to date with the latest news.
Achraf Ait Sidi Hammou

Achraf Ait Sidi Hammou

Fullstack Dev – Writing about GraphQL
Paris