GraphQL Mutations
GraphQL mutations create and modify objects, similar to a PUT, POST, or DELETE request in REST. Mutation requests are sent to the same endpoint as query requests and should be formatted inside the "query" property of the JSON object in the payload.
Structure
Mutations have the following structure:
- The
mutationoperation name - The mutation field name, such as
productsCreateProduct - The input data to use in the mutation (passed as an argument), such as the information for a new product
- A selection of return fields that should be included in the response, such as the ID of the successfully created Product, Purchase Order etc.
Structure
mutation {
mutationName(arg: "Data") {
return fields
}
}
Example
POST /graphql
Query
mutation {
productsCreateProduct(input: { product: { skuCode: "AX123", name: "King size bed" }, accountUuid: "a7353774-9795-434b-a1f8-7582b6afd345" }) {
product{
id
skuCode
name
}
}
}
Note: even though this is not a GraphQL Query, the mutation syntax must be inside a "query" property inside the JSON object that you send to GraphQL. If you're not using a client library to format the calls to the API for you, make sure that you handle this correctly. This is a GraphQL standard. Consult the official docs for more info.
Input data
As we saw above, mutations require input data to create/update a new/existing object, or the ID of an object to delete. For mutations that might require a substantial data object, the schema provides a dedicated input object type.
For example the productsCreateProduct mutation requires an input argument, which accepts a CreateProductInput object. This type defines all the fields that can be used to create a product.
Some of the input fields are required while others are options. Below we see an example for the productsCreateProduct mutation. When creating a product only skuCode and name for the ProductInput are required, but the CreateProductInput tells you that you also need to provide your accountUuid.
mutation {
productsCreateProduct(input: {
product: {
skuCode: "AX123",
name: "King size bed"
},
accountUuid: "a7353774-9795-434b-a1f8-7582b6afd345"
}) {
# ...
}
}
Returned data
Each mutation provides a set of fields that can be returned in the response. For example, one of the return fields available for the productsCreateProduct mutation is the Product object that was created by a successful mutation. Remember, this object is not the same as the input variables we provided, it is its own object with a type and a schema.
As with a GraphQL query, you can select the fields on the new object that you want to include in the response.
Remember: one of the advantages of GraphQL is that the client can request the data they need. You can use the return values of mutations to store Zencargo-generated information, such as our internal IDs, or to retrieve other information about something you've just updated. Refer to the mutation documentation to see what objects are returnable.
Each mutation also returns the errors field. The errors field returns information about errors when a mutation fails. You should include the errors field with each mutation to make it easier to troubleshoot failed mutations.
The errors field contains a message fields that provides a basic message for the failure, and a path field giving information about a specific field in the mutation tree that caused the failure e.g. skuCode. Checking the documentation for productsCreateProduct shows us that we can add an errors field to our return object, which itself will be an array of Error objects. We could update our GraphQL to include these like this:
mutation {
productsCreateProduct(input: {
#...
}) {
product {
id
name
skuCode
}
errors {
path
message
}
}
}
Create a Product
Let's go ahead and run some real mutations in your staging environment. To follow along, you should either use the API console or you will need to correctly format these requests using the method outlined in your first GraphQL call.
Creating your product catalogue is generally the first step for integrations, so let's try that. The absolute minimum that Zencargo needs to create a product is the name and SKU code. You're almost certainly going to be required to send us more than this to enable us to plan, book and track your products, but let's start with the basics.
Replacing the accountUuid for your own, make a call to the API either using the console by pasting this into the query box, or by formatting this correctly in your own client.
POST /graphql
Query
mutation {
productsCreateProduct(input: {
product: {
skuCode: "AX123",
name: "King size bed"
},
accountUuid: "a7353774-9795-434b-a1f8-7582b6afd345"
}) {
product {
id
name
skuCode
}
errors {
path
message
}
}
}
Note: remember that even though this is not a GraphQL "query", the mutation syntax must be inside a "query" key inside the JSON object that you send to GraphQL.
Successful JSON Response
{
"data": {
"productsCreateProduct": {
"errors": [],
"product": {
"id": "711607e0-d9cb-41fa-9210-330dd75f79bb",
"name": "King size bed",
"skuCode": "AX123"
}
}
}
}
Congratulations! You've created your first product successfully (no errors were returned, product data is returned)! As you can see, we got back the data that we requested about the resource that was created inside Zencargo - if we wanted to, we could specify more information as part of the return object. This is one huge benefit of GraphQL: the client defines the data they need.
Another thing to note is that the product ID is generated by Zencargo, so you likely want to persist this somewhere so that you can query for it later. For products, this is pretty much the only Zencargo-generated field, but for other objects there are many more.
Because not everything is kittens and rainbows, let's see what happens when a creation fails. Try the same operation again, specifying a duplicate SKU code.
POST /graphql
Query
mutation {
productsCreateProduct(input: {
product: {
skuCode: "AX123",
name: "King size bed"
},
accountUuid: "a7353774-9795-434b-a1f8-7582b6afd345"
}) {
product {
id
name
skuCode
}
errors {
path
message
}
}
}
Failed JSON Response
{
"data": {
"productsCreateProduct": {
"errors": [
{
"message": "has already been taken",
"path": "skuCode"
}
],
"product": null
}
}
}
SKU codes on Zencargo must be unique, and you just attempted to create a product with an already-present SKU code. You can see from the product attribute here that nothing was created and we have returned no product. Also you see a list of errors that may have prevented the mutation from being successful - in our case a path was returned because our error relates to a specific field that was not valid, as opposed to a more general server error.
Update a Product
As your SKU catalogue changes, sometimes attributes that are relevant to Zencargo will change.
To handle this, you can call a different mutation, productsUpdateProduct, with new input data. The data required to perform a query are defined by the relevant Input type, which in this case is updateProductInput.
Let's imagine we want to pass the HS code of the King size bed we've created above to Zencargo, so that Zencargo can effectively clear customs on your behalf. To do this, we're going to use the Product ID that was returned from the example above:
POST /graphql
Query
mutation {
productsUpdateProduct(input: {
id: "711607e0-d9cb-41fa-9210-330dd75f79bb",
product: {
hsCode: "0803901000"
}
}) {
product {
id,
skuCode,
hsCode
}
errors {
path
message
}
}
}
You can see here that we're passing in our input data to a new mutation just as before, but this time we are requesting different fields in the result: id, skuCode and the hsCode that we have just updated.
JSON Response
{
"data": {
"productsUpdateProduct": {
"errors": [],
"product": {
"id": "711607e0-d9cb-41fa-9210-330dd75f79bb",
"skuCode": "AX123",
"hsCode": "0803901000"
}
}
}
}
We've just successfully updated the existing product with a new HS code. Note also that the variables did not contain any reference or change to skuCode, but because we specified skuCode in the return object, we got that returned to us.
Next Steps
We've seen so far how to provide input data in mutations and query data in queries by inlining them in our requests. There's another way to provide these data, using variables. You can find more about how to do that in the next section.
- GraphQL variables - Learn how to simplify and re-use GraphQL requests through variables.