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:
// 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 
GET /user/123/age
> 23
- Option B: merge 
agewithname 
GET /user/123/nameAndAge
> { "name": "techbos", "age": 23 }
- Option C: return the entire 
userobject and let client pick whichever fields they want 
GET /user/123
> { "name": "techbos", "age": 23, "id": 123, "location": "Seattle", ... }
- Option D: use query params
 
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 
nameandagerequires 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:
{
  "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.
// 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:
// 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':
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 
Schemaand 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 aUserorPostcontains, and use a rootQuerytype to define how client can query data using params such asuserId. 
# 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.smartqlfile, and use static code checking tools such asTypeScriptorFlowto make sure client code follows the schema. For example, if we access user'sagewithout null-checking, or if we query auserobject with a string-typeid, 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 🏀
If you enjoy this post, please click ❤️ and follow me on twitter @tech_bos!
    
Top comments (0)