Introduction to GraphQL

😼 So what is GraphQL exactly?

  • A query language for your API.
  • GraphQL gives the power to ask for exactly what we need and nothing more.
  • Get as many as resources in a single request.
  • Evolve your API’s without versions.
  • GraphQL makes it easy to build powerful tools like GraphiQL by leveraging your API’s type system.

🐙 Concepts of GraphQL

  • Queries & Mutations - GraphQL queries are so much easier to request data than a REST API.
  • Schema & Types, Variables, Arguments - GraphQL has its own schema & type system which we are already familiar with (String, Int, [] etc.).
  • Resolver - is responsible for mapping the query to a function.
  • Validation - By using the type system, it is easy to determine whether a GraphQL query is valid or not.
  • Execution - After being validated, a GraphQL query is executed by a GraphQL server which returns a result that mirrors the shape of the requested query, typically as JSON.
  • Introspection - It’s often useful to ask a GraphQL schema for information about what queries it supports.

🐣 Steps to run the demo

1. Clone the repo
git clone https://github.com/gokulkrishh/introduction-to-graphql
2. Install dependencies
yarn or npm install
3. Run
yarn or npm start

🎅🏻 Demo

🧤 Libraries I used for demo

1. Variables, Arguments & Types

Like any other programming language, GraphQL has variables, arguments. Let’s see some examples.

Types

The most basic components of a GraphQL schema are object types, which just represent a kind of object you can fetch from your service, and what fields it has. If you are a web developer, you can relate this to flow or typescript.

Example:
type Person {
  name: String!
}
  • String! - name property is a non-nullable string. Meaning you will always give a value to this property.
  • More types.

Arguments

We can pass arguments to any query.

Example:
query user {
  getUser(id: 1) {
    name
    age
    gender
    picture
  }
}

Variables

So far, we have been writing all of our arguments inside the query string. But in most applications, the arguments to fields will be dynamic.

Example:
{
  "userId": 1
}

query:

query user($id: Int!) {
  getUser(id: $id) {
    name
    age
    gender
    picture,
    about
  }
}

2. 🤔 Queries (GET API’s)

1. What is better than a Hello World 🤪

query helloworld {
  hello
}

Result:

{
  "data": {
    "hello": "Hello World"
  }
}

2. To get all the users from dummy JSON.

query getAllUsers {
  getUsers {
    name
    age
    gender
    picture
  }
}

Resolver getUsers:

const getUsers = args => {
  const { gender } = args;
  if (gender) return users.filter(user => user.gender === gender);
  else return users;
};

Result:

{
  "data":{
    "users":[
      {
        "name":"Price Weber",
        "age":37,
        "gender":"male",
        "picture":"http://placehold.it/32x32"
      },
      {
        "name":"Pennington Parsons",
        "age":22,
        "gender":"male",
        "picture":"http://placehold.it/32x32"
      },
      {
        "name":"Yesenia Galloway",
        "age":36,
        "gender":"female",
        "picture":"http://placehold.it/32x32"
      }
    ]
  }
}

3. To get a single user based on an id.

query user {
  getUser(id: 1) {
    name
    age
    gender
    picture
  }
}

Resolver getUser:

export const getUser = args => {
  const { id } = args;
  const user = users.filter(user => user.id === id);
  if (user.length === 1) return user[0];
  else return `User not found for the id ${id}`;
};

Result:

{
  "data":{
    "user":{
      "name":"Price Weber",
      "age":37,
      "gender":"male",
      "picture":"http://placehold.it/32x32"
    }
  }
}

3. 🍔 Mutations

Most discussions of GraphQL focus on data fetching, but any complete data platform needs a way to modify server-side data as well. It is analogous to performing HTTP verbs such as POST, PATCH, and DELETE. Just like queries, mutation should have mutation instead of query with some id or something.

Examples: open localhost:3000/graphql to try the below.

Create a new user: (POST API 🤪)

variables:

{
  "name": "JEDI",
  "age": 25,
  "gender": "male"
}

mutation:

mutation user($name: String!, $age: Int!, $gender: String) {
  createUser(name: $name, age: $age, gender: $gender) {
    name
    age
    gender
  }
}

Resolver for createUser:

const createUser = args => {
  const { name, age, gender } = args;
  const user = users.filter(user => user.name === name); // users from DB
  if (user.length === 0) {
    return user; // Save in DB and return
  }
  else return `A user with that name already exists.`;
};

Result:

{
  "data":{
    "createUser":{
      "name":"JEDI",
      "age":25,
      "gender":"male"
    }
  }
}

Update existing user details: (PUT API 😁)

variables:

{
  "id": 1,
  "name": "JEDI 🙃",
  "age": 26
}

mutation:

mutation user($name: Int!, $name: String!) {
  updateUser(name: $name, age: $age, gender: $gender) {
    name
    age
  }
}

Resolver for updateUser:

const updateUser = args => {
  const { id, name, age, gender } = args;
  const user = users.filter(user => user.id === id);
  if (user.length === 1) {
    return user; // Save the updates in DB and return
  }
  else return `User doesn't exist for id ${id}.`;
};

Result:

{
  "data":{
    "updateUser":{
      "name":"JEDI 🙃",
      "age":25
    }
  }
}

Delate a user: (DELETE API 😜)

variables:

{
  "id": 1
}

mutation:

mutation user($id: Int!) {
  deleteUser(id: $id) {
    id
    name
    age
    gender
  }
}

Resolver deleteUser:

const deleteUser = args => {
  const { id } = args;
  const user = users.filter(user => user.id === id);
  if (user.length === 1) {
    return user; // Delete from DB and return user or return ok
  }
  else return `User doesn't exist for id ${id}.`;
};

Result:

{
  "data":{
    "deleteUser":{
      "id":1,
      "name":"Price Weber",
      "age":37,
      "gender":"male"
    }
  }
}

4. 🥊 Test Cases for GraphQL.

If are wondering how to write test cases for GraphQL. Here is an example for you starWarsValidation-test.js.

5. 🐷 Limitations of GraphQL

  • Specific Response Structure may be required - In GraphQL the response matches the shape of the query, so if you need to respond in a very specific structure, you’ll have to add a transformation layer to reshape the response.

  • Handling File Upload - There is nothing about file upload in the GraphQL specification and mutations doesn’t accept files in the arguments.

  • Cache at Network Level - Because of the common way GraphQL is used over HTTP (A POST in a single endpoint), cache at network level becomes hard. A way to solve it is to use Persisted Queries.

  • Rate Limiting - Limiting the API call’s to the particular query is the problem in GraphQL. Github recently introduced GraphQL with the different approach to solving this issue. Take a look here.

6. 🏆 References

Thanks for reading so far 😙. Share this post if you liked it.

Gokulakrishnan Kalaikovan

Gokulakrishnan Kalaikovan

Web Developer &Google Developer Expertbased in India, Bangalore.