1.4: Mutations

1.4: Mutations

Any complete API specification needs to offer the ability not only to query data, but also to create and update it.

Any API backend could, of course, be programmed to make data changes in response to any particular request, but a good protocol will have a formal separation between requests that do and do not alter data. REST convention includes the expectation that GET requests will only query data, while POST or PUT requests will change it. In the case of GraphQL, data-modifying queries are distinguished by the mutation keyword that corresponds with a different root type in the schema defined at the server.

While we can't try mutations live (swapi-graphql exposes none), we can look at a mutation for creating a Person record in the imaginary schema we've been using:

mutation doCreatePerson(
    $person: PersonInput!, 
    $favoriteFoodIds: [ID], 
    $friendIds: [ID]
) {
    createPerson(
        person: $person,
        favoriteFoodIds: $favoriteFoodIds,
        friendIds: $friendIds
    ) {
        id
        name
        createdAt
    }
}

We could imagine the above mutation being sent in a request along with the following variables dictionary:

{
    "person": {
        "name": "Sally",
        "height": 68
    },
    "favoriteFoodIds": [
        "123",
        "456"
    ],
    "friendIds": [
        "789"
    ]
}

And finally, we might receive a response like this:

{
    "data": {
        "createPerson": {
            "id": "67890",
            "name": "Sally",
            "createdAt": "2023-01-01T12:30:01.000000Z"
        }
    }
}

The chief thing to note about the above example is that, apart from the use of the mutation keyword instead of query, the syntax is identical to a query! Just like queries, the mutation includes:

  • An arbitrary operation name (doCreatePerson)
  • A list of variables (e.g., $person)
  • An initial field (createPerson) with arguments (e.g., person, set to the value of $person) in parentheses
  • A sub-selection of fields in braces

The fields sub-selection allows you to flexibly define the fields you would like returned (from what our imaginary schema presumably defines as the type of createPerson - the Person type) after the mutation is completed. Thus we have one unified syntax, and there is very little new to learn for mutations.

We've noted how fields defined in a GraphQL schema start on a root type for queries (usually called Query). Similarly, another root type exists for mutations (usually called, unsurprisingly, Mutation). Our example above presumes a field called createPerson that has been defined on that root type.

A few other notes about the above example:

  • The type for our $person variable is specified as PersonInput. When non-scalar types are available for field arguments, their definitions are separate from the usual object types returned from queries.
  • The ! character suffixing PersonInput indicates the variable is required.
  • The square brackets ([]) around the ID type specified for $favoriteFoodIds and $friendIds indicate an array of that type rather than a single value.
Complete and Continue