💡Reinvent GraphQL using RESTful - Learn GraphQL from the perspective of RESTful in 4 mins.
Tags: graphql, webdev, beginners, javascript
Published: 2019-09-25 15:52:19

*For beginners, GraphQL may seem complex on the surface but it really isn't. This post shows how you can learn core concepts of GraphQL by progressively inventing them in RESTful API.*

Let's go back to year 2014. GraphQL isn't a thing yet and our AWESOME-REST-API server has just gone live with a simple RESTful API that returns user `name` by `id`:

```javascript
// A REST API to get user name by user id
GET /user/:id/name
// e.g., get user 123's name
GET /user/123/name
> techbos
```

One day we decide to also return user's `age`. So we come up with 4 options:

- Option A: create a new endpoint for `age`

```javascript
GET /user/123/age
> 23
```
- Option B: merge `age` with `name`
```javascript
GET /user/123/nameAndAge
> { "name": "techbos", "age": 23 }
```

- Option C: return the entire `user` object and let client pick whichever fields they want
```javascript
GET /user/123
> { "name": "techbos", "age": 23, "id": 123, "location": "Seattle", ... }
```

- Option D: use query params
```javascript
GET /user/123?fields=name,age
> { "name": "techbos", "age": 23 }
```

It's easy to see option A, B, C has its issues:

- Option A: Fetching `name` and `age` requires client to make two http requests.
- Option B: Over time we can end up with hundreds of 'combination' endpoints.
- Option C: A lot of wasted bandwidth.

Option D seems reasonable. So we decide to give it a try and improve it as we go:

# Use a JSON-like Request Body

Firstly, `?fields=name` works fine for simple fields, but gets complicated when we have object/nested fields, e.g., `first` and `last` of `name` object below:

```javascript
{
  "name": {
    "first": "tech",
    "last": "bos",
  }
}
```
To solve this, we replace the url query param `?fields=` with a *JSON-like request body*. We also use `POST` instead of `GET`. 

```javascript
// Replace ?fields=name,age with request body
POST /user/123
// JSON-like request body for selecting fields
{
  name {
    first
  }
  age
}
// response
> {
    "name": {
      "first": "tech"
    },
    "age": 23
  }
``` 
This gives us a clean way of telling server which fields to return. Neat.

# Batch Multiple Requests

Next, we notice that our client code always fetches `user` and `posts` at same time using two separate requests:

```javascript
// Get user 123's first name and age
POST /user/123 { name { first }, age }
// Get user 123's post title and content
POST /posts/user/123 { title, content }
```

Can we do it with only one request? Sure. We can merge them into one single request body, and use a single endpoint `/smartql`, which stands for 'Smart Query Language':

```javascript
POST /smartql
// One request body to query for two things
{
  // Replaces /user/123
  user(id: 123) {
    name {
      first
    }
    age
  }
  // Replaces /posts/user/123
  posts(userId: 123) {
    title
    content
  }
}
// response
> {
    "user": {
      "name": {
        "first": "tech"
      },
      "age": 23,
    },
    "notifications": [{
      "title": "Notification 1",
      "content": "Super important",
    }, {
      "title": "Notification 2",
      "content": "Not very important",
    }],
  }
```
Now we can batch-fetch multiple endpoints in a single request. Neat.

# Make API Strong Typed

As our API grows, it becomes more and more difficult for front- and back-end engineers to sync up on the API changes. Our API doc seems always outdated, and it's easy to cause bugs with any API changes.

For example, we change the `age` field to null-able, and this causes a lot of client-side crashes because the code assumes `age` to always be valid. To fix this:

- First, on the server side, we create a data `Schema` and use that to make sure any data that goes in or out of our server must follow the expected format. E.g., we can define what fields a `User` or `Post` contains, and use a root `Query` type to define how client can query data using params such as `userId`.

```graphql
# Data type for a user
type User {
  name: Name! # non-null!
  age: Int    # nullable
}

# Data type for a user name
type Name {
  first: String
  last: String
}

# Data type for a notification
type Post {
  title: String!
  content: String
}

# Root query type for request body
type Query {
  user(id: Int!): User
  posts(userId: Int!): [Post] # an array of Posts
}
```

- Next, on the client side, we download all the type schemas from server into a `schema.smartql` file, and use static code checking tools such as `TypeScript` or `Flow` to make sure client code follows the schema. For example, if we access user's `age` without null-checking, or if we query a `user` object with a string-type `id`, we will get an error from the type checking.

With this solution we never need to maintain an API doc, as the API itself becomes a living doc.

We are all so very much happy about this new `SmartQL`. After a year, we decide to open source it under the name of `GraphQL` *because why not*. And this is how GraphQL all started... 

Just kidding 🙃

**I hope this post help you understand the key concepts of GraphQL. You can play with the sample GraphQL queries above in [🏀 getd.io's playground 🏀](https://getd.io/workspace?r=%7B%22requestMode%22%3A%22CLOUD%22%2C%22method%22%3A%22POST%22%2C%22url%22%3A%22https%3A%2F%2Fgetd.io%2Fgraphql%22%2C%22params%22%3A%5B%5D%2C%22authorization%22%3A%7B%22type%22%3A%22NONE%22%7D%2C%22headers%22%3A%5B%7B%22isEnabled%22%3Atrue%2C%22key%22%3A%22content-type%22%2C%22value%22%3A%22application%2Fjson%22%7D%5D%2C%22body%22%3A%22%7B%5Cn%20%20%5C%22operationName%5C%22%3Anull%2C%5Cn%20%20%5C%22variables%5C%22%3A%7B%7D%2C%5Cn%20%20%5C%22query%5C%22%3A%5C%22%7B%5C%5Cn%20%20demoUser(id%3A%20123)%20%7B%5C%5Cn%20%20%20%20name%20%7B%5C%5Cn%20%20%20%20%20%20first%5C%5Cn%20%20%20%20%20%20last%5C%5Cn%20%20%20%20%7D%5C%5Cn%20%20%20%20age%5C%5Cn%20%20%20%20posts%20%7B%5C%5Cn%20%20%20%20%20%20title%5C%5Cn%20%20%20%20%20%20content%5C%5Cn%20%20%20%20%7D%5C%5Cn%20%20%7D%5C%5Cn%7D%5C%5Cn%5C%22%5Cn%7D%22%2C%22formData%22%3A%5B%5D%2C%22formUrl%22%3A%5B%5D%2C%22bodyEditorFormat%22%3A%22json%22%2C%22requestBuilderTab%22%3A%22body%22%7D)** 

**If you enjoy this post, please click ❤️ and follow me on twitter [@tech_bos](https://twitter.com/tech_bos)!**