Atomic set and increment operators for GraphQL mutations
When using GraphQL, writes or updates to data are made via mutations. In this post we will discuss the update mutations and related operators that are available on the Hasura GraphQL Engine.
TL;DR:
- Hasura allows you to update multiple objects in one shot using a powerful
where
argument which allows you to create a boolean filter to select the objects to be updated - Hasura supports a
set
operator to set or replace an existing value - Hasura supports an
increment
operator to atomically increment numerical values by a given amount to prevent common race conditions where multiple clients are incrementing the same attribute - Hasura supports native JSON/JSONB operators that you can use to modify attributes in a JSON/JSONB value of a postgres column without needing to
set
the full JSON value
Schema and generated mutations
Let’s consider the following schema for our example:
When the above tables are tracked in Hasura GraphQL Engine, it will automatically generate the following update mutations:
As you can see above, there are 3 key arguments in the update mutation, _set
, _inc
and where
.
where
is a boolean expression, which is mandatory. where
will help you select the rows on which the mutation is to be applied. Otherwise the update mutation will have to be applied for all rows in the table or only on one particular row, either of which are not particularly flexible options. You can read more about “bulk update” mutations.
Using the "_set"
operator
The _set
operator “sets” the value of one or more columns of a row by replacing the existing value. Consider 2 illustrative examples below:
Let’s say we want to update the title and content of an article that the user is editing. We can run the following GraphQL query using the _set
operator (assuming we know the id of the article) :
If we want to update all articles of an author, i.e. update data using nested objects’ fields, our query would be like the following:
Using the "_inc"
operator
The _inc operator atomically increments the existing value of one or more columns by a given amount. This operator is only for columns that have a “numeric” type, like integer, numeric, float, double etc.
Let’s say we want to update the likes of an article. As it is a numeric field, we can take advantage of the _inc
operator. We can run the following GraphQL query (assuming we know the id of the article) :
Why do we need “_inc”?
The advantage of using _inc
is we can avoid race conditions.
If we were using _set
directly — fetch previous likes, increment them on the client, and then run a set query with the new value — which can lead to race conditions when two clients simultaneously fetched the same likes and tried to update. One of our likes will be lost. As the _inc
query is guaranteed to run atomically, we know that using that we will not run into race conditions.
Using mutations to update JSON/JSONB data
Hasura GraphQL Engine has first-class support for updating JSON/JSONB data in your Postgres tables. You don’t have to form the JSON in your client and use _set
. You can directly use any of _prepend
, _append
, _delete_key
, _delete_elem
, and _delete_at_path
operators to update nested keys and values in your JSON data.
You can read more about these operators in this blog