Skip to main content

Mutations

Mutations in GraphQL are used to modify data (e.g. create, update, delete data). Mutations return an instance of the object you just modified, so you can query the data you changed.

Below are a couple of examples how to create, update, delete and restore a contact. The same four mutations follow the same pattern for most other objects - see the API Reference for the full list.

Create a new contact

mutation CreateContact($input: CreateContactInput!) {
createContact(input: $input) {
id
name
lastName
displayName
createdAt
}
}
Query variables
{
"input": {
"type": "PERSON",
"name": "John",
"lastName": "Doe"
}
}

Only name is required on CreateContactInput - every other field is optional.

Update an existing contact

Pass the id of the record you are changing inside input, together with only the fields you want to change. Fields you leave out keep their current value; they are not set to null.

mutation UpdateContact($input: UpdateContactInput!) {
updateContact(input: $input) {
id
name
lastName
displayName
}
}
Query variables
{
"input": {
"id": "1",
"name": "Peter"
}
}

Delete a contact

Deleting is reversible: the record is soft-deleted and deletedAt is set to the time it was removed.

mutation DeleteContact($id: ID!) {
deleteContact(id: $id) {
id
deletedAt
}
}
Query variables
{
"id": "1"
}

Restore a deleted contact

Restoring clears deletedAt and brings the record back.

mutation RestoreContact($id: ID!) {
restoreContact(id: $id) {
id
deletedAt
}
}
Query variables
{
"id": "1"
}

Deleting is reversible

Records are not erased. Deleting sets deletedAt and hides the record from lists; restoring clears it again. Nothing is lost in between, which is why restoreContact can work at all.

The practical consequence is that a deleted record is still there, and you can still reach it. List queries leave deleted records out by default, so use the trashed argument to see them:

query DeletedContacts {
contacts(first: 25, trashed: ONLY) {
edges {
node {
id
displayName
deletedAt
}
}
}
}

See narrowing down a list for the other trashed modes.

When a mutation fails

A failed mutation is not an HTTP error. Like most of the API it answers with HTTP 200 and an errors array, so check the body rather than the status code.

Validation failures name the offending fields under extensions.validation, keyed by their path in the input:

{
"errors": [
{
"message": "Validation failed for the field [createContact].",
"extensions": {
"validation": {
"input.name": ["The input.name field is required."]
}
}
}
]
}

Read extensions.validation rather than parsing the message: it tells you exactly which inputs to correct, and it is stable in a way the human-readable sentence is not.

Validation is only one of the things a mutation can refuse. See error handling for the full set and how to tell them apart.

Important!

GraphQL can return data and errors in the same response. If you send several mutations in one document and one of them fails, the others may still have run, and their results will be sitting in data alongside the error. Never assume an errors array means nothing happened.

Sending more than one mutation

You can put several mutations in one document, and they run in sequence:

mutation CreateTwo($a: CreateContactInput!, $b: CreateContactInput!) {
first: createContact(input: $a) {
id
}
second: createContact(input: $b) {
id
}
}

Be aware that each root mutation field counts separately against the mutation rate limit - the document above costs 2, not 1. Combined with the partial-failure behaviour described above, one mutation per request is usually the easier thing to reason about.