GraphQL List - How to use arrays in GraphQL schema (GraphQL Modifiers)
Tags: webdev, react, graphql, node
Published: 2020-02-12 16:26:02

---
title: "GraphQL List - How to use arrays in GraphQL schema (GraphQL Modifiers)"
published: true
description: "GraphQL List, how to use arrays in your GraphQL schema. Applying array on GraphQL type. Implementing GraphQL modifiers. How to design mutations and queries with a list of items as an argument and returned values. GraphQL non-null modifier. Required fields in GraphQL schema. How to compose modifiers. GraphQL list modifier."
tags: webdev, react, graphql, node
canonical_url: https://atheros.ai/blog/graphql-list-how-to-use-arrays-in-graphql-schema
---

## Introduction

It is often common practice in REST APIs to return a JSON response with an array of objects. In GraphQL we would like to follow this pattern as well. In this article we will go through modifiers, a special group of types which allows us to modify the default behaviour of other types. In GraphQL we deal with various groups of types. These groups are as follows:
* <a href="https://atheros.ai/blog/how-to-use-graphql-enum-type-and-its-best-practices" title="How to design GraphQL scalars">Scalars</a> and <a href="https://atheros.ai/blog/how-to-design-graphql-custom-scalars" title="How to design GraphQL custom scalar" >custom scalars</a>
* <a href="https://atheros.ai/blog/how-to-use-graphql-enum-type-and-its-best-practices" title="GraphQL Enums">Enums</a>
* <a href="https://atheros.ai/blog/input-object-type-as-an-argument-for-graphql-mutations-and-queries" title="Input object types">Objects and input object types</a>
* <a href="https://atheros.ai/blog/graphql-interfaces-and-unions-how-to-design-graphql-schema" title="Interfaces and unions">Abstract types - Interfaces and union types</a>
* **Modifiers**

It may be helpful first to go through the articles above. After gaining a fundamental understanding of other types such as scalars and object types you can then move on to modifiers. Next, we can start working on the project set-up so that we can test our queries. We assume that <a title="Npm website"  href="https://yarnpkg.com">npm</a>, <a title="Git website"  href="https://git-scm.com/">git</a> and <a title="Node.js website"  href="https://nodejs.org/en/">Node.js</a> versions higher than 8 are already installed on your computer. Now you can execute this command in your shell

``` bash
git clone git@github.com:atherosai/graphql-gateway-apollo-express.git
```

install dependencies with

``` bash
npm i
```

and start the server in development with 

``` bash
npm run dev
```

Then you can move to <a title="Localhost environment" href="http://localhost:8000/graphql">GraphQL Playground</a> to execute the queries available in this article. In the model project, we use the **in-memory** database with fake data for executing our queries.

## Model schema

Let’s first consider this model schema, which was printed with the <a title="Print schema definition" href="https://graphql.org/graphql-js/utilities/#printschema">printSchema</a> function from graphql-js utilities. The model schema in the repository is built with a class-based approach using the graphql-js library. It is often much clearer to view the whole schema written in Schema definition language (SDL). For some time now, SDL has been a <a  title="SDL in official specification" href="https://github.com/facebook/graphql/pull/90 ">part of the specification</a> and it is often used to build the schema itself using the build schema utility or the library called <a  title="GraphQL tools github" href="https://github.com/apollographql/graphql-tools">graphql-tools</a>

``` graphql
"""Input payload for creating user"""
input CreateUserInput {
  username: String!
  email: String
  phone: String
  firstName: String
  lastName: String
  role: UserRoleEnum = ACCOUNTANT
}

"""User type definition"""
type CreateUserPayload {
  user: User!
}

"""User type definition"""
type CreateUsersPayload {
  users: [User]
}

"""An ISO-8601 encoded UTC date string."""
scalar DateTime

type Mutation {
  createUser(input: CreateUserInput!): CreateUserPayload
  createUsers(input: [CreateUserInput!]!): CreateUsersPayload
}

type Query {
  users(role: UserRoleEnum): [User!]!
}

"""User type definition"""
type User {
  id: ID!
  username: String!
  email: String
  phone: String
  firstName: String
  lastName: String
  role: UserRoleEnum!
  createdAt: DateTime!
  updatedAt: DateTime
}

enum UserRoleEnum {
  ADMIN
  ACCOUNTANT
}
```

We can see that we have defined one output object type called **User** with the following fields: **id**, **username**, **email**, **phone**, **firstName**, **lastName**, **createdAt**, **updatedAt**. The id field is typed as an ID scalar and other fields are typed as Strings. We’ve also defined the queries **user** and **users**. The user query returns the User object based on the passed id. The users query then returns a list of users. We have also defined the non-required enum type role, which is used in the users query as an argument for filtering the result. In this simple schema we used modifiers quite a lot. In the rest of the article, we will go through these use cases.

## Modifiers

First, let’s formally define modifier. As we have already mentioned, **modifier** is a special group of types in **GraphQL**. These types can be defined as follows:

> A Modifier modifies the type to which it refers.

From this definition, it is clear that we always need to define the type to which we are applying the modifier. In current **GraphQL** specification, we have these two types of modifiers. Each of the modifiers is classified as a separate type:
* **List**
* **Non-Null**

The List modifier will be our main focus in this article. It will allow us to define if we would like to return a sequence of types. A Non-Null modifier allows us to define if the type/field is required. This can be null (default behavior in GraphQL) or is required and the GraphQL server raises an error. In this article, we will focus mainly on **List** modifiers and leave a more in-depth discussion of **Non-Null** modifiers for another article.

## List
In general, a GraphQL list represents a **sequence of values**. It is possible to view these values as arrays (e.g. in Javascript), although the analogy is not completely precise. As we mentioned a list keeps items in an order. In SDL the list modifier is written as **square brackets** with the wrapped instance of the type in the bracket. In our schema, we used the list modifier to define that if we call the query users, it returns a sequence of types of **User** from the database. This is achieved by defining the schema as follows:
``` graphql
type Query {
  user(id: ID!): User
  users(role: RoleEnum): [User!]!
}
```

By calling query users we expect to return a list of users. Let’s see how this looks when we use the <a href="https://github.com/graphql/graphql-js" title="GraphQL schema builder">graphql-js</a> library. The queries in our repository are defined as follows:
``` javascript
import {
  GraphQLList,
  GraphQLNonNull,
} from 'graphql';
import { getUsers } from '../../operations/users-operations';
import User from './UserType';
import UserRoleEnum from './UserRoleEnumType';

const UserQueries = {
  users: {
    type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(User))),
    args: {
      role: {
        type: UserRoleEnum,
      },
    },
    resolve: (_source, { role }) => {
      const result = getUsers();
      if (role != null) {
        return result.filter((user) => user.role === role);
      }
      return result;
    },
  },
};

export default UserQueries;
```

We can see that we achieve the same functionality as with SDL. The **GraphQLList** class represents the List. We have applied the instance of this class to the instance of **User**. Now we are able to fetch the data by executing the users query in GraphQL Playground with the **Play** button.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/r7ag0csl7xht7od48nt6.png)

We should retrieve this data and obtain users as a list.

``` json
{
  "data": {
    "users": [
      {
        "id": "7b838108-3720-4c50-9de3-a7cc04af24f5",
        "firstName": "Berniece",
        "lastName": "Kris",
        "username": "Ana_Quigley"
      },
      {
        "id": "66c9b0fd-7df6-4e2a-80c2-0e4f8cdd89b1",
        "firstName": "Bradly",
        "lastName": "Lind",
        "username": "Winona_Kulas12"
      },
      {
        "id": "718590a1-33ac-4e61-9fef-b06916acd76b",
        "firstName": "Leila",
        "lastName": "Schowalter",
        "username": "Isabell.Kautzer"
      },
      {
        "id": "411df0f3-bb2c-4f5f-870f-3db9c30d754f",
        "firstName": "Laila",
        "lastName": "Breitenberg",
        "username": "Christophe.Oberbrunner"
      },
      {
        "id": "e1254480-d205-4be8-abfa-67ad7dcd03fb",
        "firstName": "Joe",
        "lastName": "Crist",
        "username": "Dahlia.Gerhold56"
      },
      {
        "id": "d0087200-9621-4787-a3db-cebbede163e6",
        "firstName": "Bettye",
        "lastName": "Bartoletti",
        "username": "Thad_Mayert"
      }
    ]
  }
}
```

The other use case for List modifiers is for designing the **createUsers** mutation, where we can add users in batch. There are multiple reasons to design the mutations in this way. We may need to add users in a transaction, therefore we cannot have a different resolver context or we just want to simplify the API or improve the performance and execute the mutation for multiple users more quickly. This is a great use case for applying the List modifier to our input payload. We can define the input object type just once like this:

``` javascript
import {
  GraphQLString,
  GraphQLInputObjectType,
  GraphQLNonNull,
} from 'graphql';
import UserRole from './UserRoleEnumType';

const CreateUserInputType = new GraphQLInputObjectType({
  name: 'CreateUserInput',
  description: 'Input payload for creating user',
  fields: () => ({
    username: {
      type: new GraphQLNonNull(GraphQLString),
    },
    email: {
      type: GraphQLString,
    },
    phone: {
      type: GraphQLString,
    },
    firstName: {
      type: GraphQLString,
    },
    lastName: {
      type: GraphQLString,
    },
    role: {
      type: UserRole,
      defaultValue: UserRole.getValue('ACCOUNTANT').value,
    },
  }),
});

export default CreateUserInputType;
```

or in SDL language

``` graphql
input CreateUserInput {
  username: String!
  email: String
  phone: String
  firstName: String
  lastName: String
}
```

and then apply List modifier to achieve the ability to pass multiple payloads in one input variable.

``` javascript

import {
  GraphQLList,
  GraphQLNonNull,
} from 'graphql';
import { isEmail } from 'validator';
import { createUser, createUsers } from '../../operations/users-operations';
import CreateUserInput from './CreateUserInputType';
import CreateUserPayload from './CreateUserPayload';
import CreateUsersPayload from './CreateUsersPayload';

const UserMutations = {
  createUser: {
    type: CreateUserPayload,
    args: {
      input: {
        type: new GraphQLNonNull(CreateUserInput),
      },
    },
    resolve: (_source, args) => {
      const { input } = args;

      if (input.email && !isEmail(input.email)) {
        throw new Error('Email is not in valid format');
      }
      return {
        user: createUser(input),
      };
    },
  },
  createUsers: {
    type: CreateUsersPayload,
    args: {
      input: {
        type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(CreateUserInput))),
      },
    },
    resolve: (_source, { input }) => {
      const createdUsers = createUsers(input);
      return {
        users: createdUsers,
      };
    },
  },
};

export default UserMutations;
```

We can execute the mutation with using inline arguments or if you prefer with <a href="https://atheros.ai/blog/graphql-quick-tip-how-to-pass-variables-in-graphiql" title="Article on using variables">using variables</a>
``` graphql
mutation {
  createUsers(input: [{lastName: "Test", firstName: "test", username: "t1est"}, {lastName: "Test", firstName: "test", username: "te2st"}]) {
    users {
        id
        firstName
        lastName
        phone
        email
        username
    }
  }
}

```

Now let’s go through the rules for result and input coercion. If you are not familiar with these terms, you can take a look at the article on <a href="https://atheros.ai/blog/graphql-scalars-and-their-input-and-result-coercion" title="Article on using scalars and input and result in coercion">scalars</a>, where we describe input and result in coercion.

## Result coercion

For the query **users**, result coercion is relevant for us as we would like to obtain an array of users from the executed query. When we coerce lists, the GraphQL server needs to ensure that the returned data from the resolver function will remain in the same order. The coercion of each item in the list is then delegated to the result coercion of the referenced type; each item of the array needs to comply to **User** type or **null** value. If it returns an object instead of array-like in this resolver function:

``` javascript
import {
  GraphQLList,
  GraphQLNonNull,
} from 'graphql';
import { getUsers } from '../../operations/users-operations';
import User from './UserType';
import UserRoleEnum from './UserRoleEnumType';

const UserQueries = {
  users: {
    type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(User))),
    args: {
      role: {
        type: UserRoleEnum,
      },
    },
    resolve: (_source, { role }) => {
      const result = getUsers();
      if (role != null) {
        return result.filter((user) => user.role === role);
      }
      return result;
    },
  },
};

export default UserQueries;
```

the GraphQL server should then raise this error
``` bash
Expected Iterable, but did not find one for field Query.users.
```

This happens if the coercion of the List modifier does not comply But what happens if some of the items in the list do not coerce properly? In that case, we handle the error in a similar manner. We return *null* instead of the value returned from the resolver function and add an error to the response.

## Input coercion

When discussing input coercion of *List* modifiers we can take into account the *createUsers* mutation and describe the behavior that raises an error. In contrast to the result coercion, where some items from the result array can be obtained even if one item is not coerced properly, in input coercion we will not be able to execute the whole mutation if one payload cannot be coerced. Let’s take a look at the following example, where we would like to pass a list of two payloads, but one payload does not comply with the input type and does not have the required *username* field. Upon executing this mutation we receive the following error:
``` bash
Argument "input" has invalid value [{username: "testtest", email: "test@test.com", firstName: "test", lastName: "test"}, {email: "test@test.com", firstName: "test", lastName: "test"}].
In element #1: In field "username": Expected "String!", found null.
```

The whole mutation fails even if only the input coercion in the input object type in one item in the list does not comply. However, it is important to emphasize that if we pass null as follows, the whole mutation will be executed. However, this depends on whether or not we applied any additional modifiers and composed the modifiers in a more complex type. We will go through this topic in the last section of this article on **Modifier composition**.

## Modifier composition

If we consider the definition of the modifier above, we know that the modifier basically creates a new type from the referenced type with additional functionality. In our case, we are adding behavior so that the result coercion will accept a list of items and not just the item itself. This is also similar to higher-order functions or the decorator pattern and in the same manner, we can chain higher-order functions or HOCs in React. We are also able to compose modifiers by applying a modifier to the type where the previous modifier is already applied. We can combine the Non-Null modifier with our List modifier in the following way. This way we basically combine three modifiers, which are chained as follows
``` bash
new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(User)))
```

This creates a special type. When using only a list modifier we are allowed to return a null value from the resolver. We can even combine the items in the array to contain null values as in this array:
``` graphql
mutation {
  createUsers(input: [{username: "testtest", email: "test@test.com", firstName: "test", lastName: "test"}, null]) {
    id
    username
    firstName
  }
}
```

But when we apply the composed modifier as above, we are only allowed to pass the array containing the objects that comply with the User type. The list above will, therefore, be rejected. The null value returned from the resolver will be also rejected. You can take a look at the table below, which contains what each modifier will allow in order to get a better idea of which combinations of modifiers are suitable for different use cases. The only rule in chaining modifiers applies to Non-null modifiers. It declares that we cannot wrap one Non-Null modifier with another Non-Null modifier.

| [User]   | [UserObject, null] | Valid   |
|----------|--------------------|---------|
| [User]   | null               | Valid   |
| [User]   | [null]             | Valid   |
| [User]   | [UserObject]       | Valid   |
| [User!]  | [UserObject,null]  | Invalid |
| [User!]  | [null]             | Invalid |
| [User!]  | null               | Valid   |
| [User!]  | [UserObject]       | Valid   |
| [User!]! | [UserObject, null] | Invalid |
| [User!]! | null               | Invalid |
| [User!]! | [UserObject]       | Valid   |
| User!!   | -                  | Invalid |

**UserObject** in this table can be equal for example to
``` json
{ lastName: "Test", firstName: "test", username: "t1est"}
```

For simplicity, we did not cover the differences between input and output coercion for these more complex types. The behavior is different only as we discussed in the result and input coercion section. If there would be different UserObject, which do not comply with User type coercion (e.g. does not have username property), there would be additional rules.

## Summary

In this article, we have covered one special group of types in GraphQL called **Modifiers**. With modifiers, we are allowed to inject special behavior into the referenced GraphQL type, add a List and other required fields, and even combine these use cases to build more complex types. Modifiers are a great tool to make elegant GraphQL schemas.

> Did you like this post? You can clone the whole repository <a href="https://github.com/atherosai/graphql-gateway-apollo-express" title="GraphQL API starter code">with examples</a>. Feel
> free to send any questions about the topic to <a href="mailto:david@atheros.ai" title="Email to ask questions">david@atheros.ai</a>.