Back
Featured image of post Understanding GraphQL Query and Mutation in Apollo Server

Understanding GraphQL Query and Mutation in Apollo Server

In this article, we will be going through the fundamental of GraphQL in Apollo Server with NodeJs

Table of contents

Introduction

Before we get started, it’s high recommend to clone the Github repo and we can follow along with the article contents.

The first step after we downloaded the repo is to checkout the the starter folder and run npm install and npm run start to start our Apollo GraphQL server locally.

We can now navigate to http://localhost:4000/ and here is how it should look like

apollo-graphql-server

We could then click on the Query your server button and it shall brings us to the GraphQL editor

Schema and Types

There are total of 5 scalar types available in GraphQL

  • ID: The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human‐readable.
  • Int: A signed 32‐bit integer.
  • Float: A signed double-precision floating-point value.
  • String: A UTF‐8 character sequence.
  • Boolean: true or false.

Here is one of the GraphQL example type in our Github repo:

type User {
  id: ID!
  name: String!
  age: Int!
  nationality: String!
  friends: [User!]
}

We now structed our User type to have a id field to be an ID scalar type which most of the time will be our unique identifier from the database. We also have the rest of the fields called name, age, nationality and friends.

The exclamation mark right beside of the scalar type is stands for non-nullable field. In other words, these field must be existed from our data source when we are querying from the GraphQL command.

For the friends field, it’s a little special as it’s an Array type. [User!] scalar type means the field will now contains whatever we have specified for the User type. It also means it’s a nullable field BUT when it also means it cannot be a empty array

Checkout the official docs if you are keen to learn more

Query

There is query ready in our starter pack. Let’s play around with that by typing our query in the GraphQL editor.

get users query

...
query GetUsers{
  users {
    id
    name
  }
}

We will first need to name our query, in this case we named it as GetUsers and it’s totally customizable and up to your own preference. Then inside the bracket, it’s gonna we what query type we are referring too. If you look at the Query type in the type-def.js file, the starter pack code has already provided an example to return a list of users. Inside the query type, instead of returning every fields provided by the query which normally what we seen for REST API. We could actually specify what are fields to be returned in our query. In this case, we only want the id and name to be returned in our query

GraphQL also smart enough to automatically create a documentation based on whatever we have defined in our Query and Mutation by explaining what fields are available in every Query and Mutation

graphql documentation

Before we actually create our own query resolver, we would need to definite it’s type in the type-def.js file. What are we going to do next is to create a resolver that return a specific user by ID.

...

const typeDefs = gql`
  type Query {
    users: [User!]!
    user(id: ID!): User!
  }

  ...
`;

Since our new resolver query is to return a specific user by ID. We would then need the id as the input parameter and the return type will be an User. We can navigate to resolver.js file and create our resolver for this query type.

Take note that we could access our resolve input variable from the second argument. eg: args.id

const { Users } = require("../data");

const resolvers = {
  Query: {
    users: () => {
      return Users;
    },
    user: (_parent, args) => {
      const id = args.id;
      return Users.find((user) => Number(user.id) === Number(id));
    },
  },
};

module.exports = { resolvers };

Outcome

We could use the following query in our GraphQL editor

query GetUser {
  user(id: "1") {
    id
    nationality
  }
}

However, there is a better way to structure the query by introducing the id variable:

query GetUser($id: ID!) {
  user(id: $id) {
    id
    nationality
  }
}

And we could specify the value of $id variable:

graphql variable

Mutation

We now understand how to query the data from GraphQL server. Let’s create some Mutation to add in a new user. The concept is almost similar to what we have for the Query type where we first need to define the input and return type of the mutation.

We can also create an input type which hold the entities when we are creating a new user

...
type Mutation {
  createUser(input: CreateUserInput!): User
}

input CreateUserInput {
  name: String!
  age: Int!
  nationality: String!
}

type-def.js

And now, we can then add in our logic for the resolver. Instead of wrapping the resolver under Query object, we should now wrap them into the Mutation object.

Also take note that we can access ou

const { Users } = require("../data");

const resolvers = {
  Query: {
    users: () => {
      return Users;
    },
    user: (_parent, args) => {
      const id = args.id;
      return Users.find((user) => Number(user.id) === Number(id));
    },
  },
  Mutation: {
    // The object key store in args will be depends on what we define in type-def. If we name it as input then it will be args.input
    createUser: (_parent, args) => {
      const id = Users[Users.length - 1].id + 1;
      const user = {
        ...args.input,
        id,
      };
      Users.push(user);
      return user;
    },
  },
};

module.exports = { resolvers };

resolvers.js

Outcome

Explaination

As you can see, it’s pretty much same with what we did for the Query and we just have to change the Query systax to Mutation and fill in the value under the variable panel.

mutation CreateUser($input: CreateUserInput! ){
  createUser(input: $input) {
    name,
    nationality,
    id
  }
}

Let’s say we have 2 different type called User and Sport. The data from User does not containing any data from Sport and vice versa. How can we join them by creating a custom field in the type?

const { gql } = require("apollo-server");

const typeDefs = gql`
  type Query {
    users: [User!]!
    user(id: ID!): User!
  }

  type Mutation {
    createUser(input: CreateUserInput!): User
  }

  type User {
    id: ID!
    name: String!
    age: Int!
    nationality: String!
    friends: [User!]
    favouriteSports: [Sport!]
  }

  type Sport {
    id: ID!
    name: String!
    minimumPlayers: Int!
    maximumPlayers: Int!
  }

  input CreateUserInput {
    name: String!
    age: Int!
    nationality: String!
  }
`;

module.exports = {
  typeDefs,
};

type-def.js

As you can see we have defined a new type called Sport as well as we have introduce a new field called favouriteSports which will return an array of Sport type. Instead of manually adding the Sport data into User, GraphQL actually allow us to create a custom type resolver, in this case will be User.

You should be able to import another constant data called Sports that are storing in the same location with Users

const { Users, Sports } = require("../data");

const resolvers = {
  Query: {
    users: () => {
      return Users;
    },
    user: (_parent, args) => {
      const id = args.id;
      return Users.find((user) => Number(user.id) === Number(id));
    },
  },
  User: {
    favouriteSports: () =>
      Sports.filter(
        (sport) => sport.minimumPlayers >= 2 && sport.maximumPlayers <= 4
      ),
  },
  Mutation: {
    createUser: (_parent, args) => {
      const id = Users[Users.length - 1].id + 1;
      const user = {
        ...args.input,
        id,
      };
      Users.push(user);
      return user;
    },
  },
};

module.exports = { resolvers };

resolvers.js

Outcome

Explaination

We are able to retrieve the Badminton, Ping Pong and Squash in every returned user data because they are fulfilling the logic we defined in the favouriteSports resolver.

Conclusion

The final source code is available in the Github final folder. Feel free to playaround and there will be more and more episode coming in for GraphQL topics.

comments powered by Disqus