From "Thinking in REST" to "Thinking in GraphQL"

A post to share my journey learning GraphQL.

Status: Work in Progress

Over the past many years, I have been developing APIs using the REST way of representing application models as resources that can be addressed through http urls and manipulated through http methods.

GraphQL does things differently.

Here are some key differences that have helped me better adjust to the new way of thinking as well as be able to explain them to my peers.

Resources vs Schemas

The REST way of thinking is to map data models to http resources. User becomes /users. Article becomes /articles. This url itself doesn't help us express anything other than the main entity that is being fetched.

In GraphQL, we instead start by thinking of a Schema. An ArticleSchema would look something like...

type Article {
  _id: String!
  slug: String!,
  title: String,
  body: String,
  isPublished: Boolean,
  publishDate: Date

Routes vs Resolvers

The next step in a REST way of thinking would be to think about how we can expose routes for our resources. This need is usually fulfilled by a http web framework like express.

In GraphQL, we think of resolvers that expose fields in our Schema; unlike exposing the entire resource behind a single route in REST, resolvers make us expose individual fields at a much granular level. This added granularity of the resolvers and the defined schems make GraphQL really simple to consume from a client pov. In order to build our schema and resolvers, we can use a GraphQL web framework like express-graphql.

CRUD vs Queries & Mutations

In REST, a client would query the resources using HTTP URLs and Methods. Fetch with GET, Create with POST, Update is PUT (or POST), Delete with DELETE - that's how we would write a CRUD API for our applications.

In GraphQL, everything is a POST. We use a Query to fetch and Mutations to create, update and delete.

A Query to fetch a list of Articles would look like....

Query: {
  allArticles: async (parent, args, { Article }) => await Article.find()

A key thing to note here is that we will use the POST body to pass the schema of the result we want. Want the article slugs and title? POST to allArticle with body set to { slug, title}

A mutation to create an article would look like...

Mutation: {
  createArticle: async (parent, args, { Article }) => await new Article(args).save()

If we used the POST body to pass the args to fetch fields in Quries, we use the POST body to pass the values to set in the fields for Mutations.

Caching, Batching etc

In the REST way, we would fallback on HTTP caching for how long to cache the queries, and Batching was more of a developer-level concern and the granularity of how much or how little to fetch was sort of arbitrary.

In GraphQL, the granularity in the resolvers along with relationships between entities defined in the Schema present an opportunity for a web framework like Apollo Server to provide high level solutions.

This post is a work in progress, more to come later...