| sidebar_position | 2 |
|---|---|
| slug | /interacting/transactional-writes |
| description | Updating multiple relationship tuples in a single transaction |
import { AuthzModelSnippetViewer, CardBox, DocumentationNotice, ProductConcept, ProductName, ProductNameFormat, RelatedSection, RelationshipTuplesViewer, WriteRequestViewer, SupportedLanguage, } from '@components/Docs';
Using, you can update multiple in a single transaction.
Updating multiple relationship tuples can keep your system state consistent.
Familiarize yourself with basic before completing this guide.
In the following , there is called tweet that can have a reader. There is another type called user that can have a follower and followed_by relationship.
tweet that can have a reader. There is another type called user that can have a follower and followed_by relationship.<AuthzModelSnippetViewer configuration={{ schema_version: '1.1', type_definitions: [ { type: 'tweet', relations: { viewer: { this: {}, }, }, metadata: { relations: { viewer: { directly_related_user_types: [ { type: 'user' }, { type: 'user', wildcard: {} }, { type: 'user', relation: 'follower' }, ], }, }, }, }, { type: 'user', relations: { follower: { this: {}, }, followed_by: { this: {}, }, }, metadata: { relations: { follower: { directly_related_user_types: [{ type: 'user' }] }, followed_by: { directly_related_user_types: [{ type: 'user' }] }, }, }, }, ], }} />
In addition:
Creating an authorization model and a relationship tuple grants a user access to an object. To learn more, read about Direct Access.
The following example uses public access. To learn more, read about Public Access.
- A : a class of objects that have similar characteristics
- A : an entity in the system that can be related to an object
- A : is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- A : a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system
- A : a group stored in that consists of a user, a relation, and an object
A call to the Write API can add or delete tuples in your store. For example, the following tuple makes tweet:1 public by making everyone a viewer:
<WriteRequestViewer relationshipTuples={[ { user: 'user:*', relation: 'viewer', object: 'tweet:1', }, ]} />
Deleting the previous tuple converts this tweet to private:
<WriteRequestViewer deleteRelationshipTuples={[ { user: 'user:*', relation: 'viewer', object: 'tweet:1', }, ]} />
By removing the tuple, we made the tweet visible to no-one, which may not be what we want.
Limitations on duplicate tuples in a single request
When using the Write API, you cannot include the same tuple (same user, relation, and object) in both the writes and deletes arrays within a single request. The API will return an error with code `cannot_allow_duplicate_tuples_in_one_request` if duplicate tuples are detected.
For example, this request would fail:
curl -X POST 'http://localhost:8080/stores/{store_id}/write' \
-H 'content-type: application/json' \
--data '{
"writes": {
"tuple_keys": [{
"user": "user:anne",
"relation": "member",
"object": "group:2"
}]
},
"deletes": {
"tuple_keys": [{
"user": "user:anne",
"relation": "member",
"object": "group:2"
}]
}
}':::tip For handling cases where you need to write tuples that might already exist or delete tuples that might not exist, check out Duplicate Writes which provides resilient write operations. :::
The Write API allows you to send up to 100 unique tuples in the request. (This limit applies to the sum of both writes and deletes in that request). This means we can submit one API call that converts the tweet from public to visible to only the user's followers.
<WriteRequestViewer relationshipTuples={[ { _description: "Anne's followers can view tweet:1", user: 'user:anne#follower', relation: 'viewer', object: 'tweet:1', }, ]} deleteRelationshipTuples={[ { _description: 'tweet:1 is no longer viewable by everyone ()', user: 'user:', relation: 'viewer', object: 'tweet:1', }, ]} allowedLanguages={[ SupportedLanguage.JS_SDK, SupportedLanguage.GO_SDK, SupportedLanguage.DOTNET_SDK, SupportedLanguage.PYTHON_SDK, SupportedLanguage.JAVA_SDK, SupportedLanguage.CURL, SupportedLanguage.RPC, ]} />
Sending multiple tuples per request can also help maintain consistency. For example, if anne follows becky, you can save the following two tuples or neither of them:
<RelationshipTuplesViewer relationshipTuples={[ { _description: 'Anne is a follower of Becky', user: 'user:anne', relation: 'follower', object: 'user:becky', }, { _description: 'Becky is followed by Anne', user: 'user:becky', relation: 'followed_by', object: 'user:anne', }, ]} />
:::info
In this case, the type user exists because users can be related to each other, so users now are a type in the system.
:::
The service attempts to perform all the changes sent in a single Write API call in one transaction. If it cannot complete all the changes, it rejects all of them.
<RelatedSection description="Check the following sections for more on how to update tuples." relatedLinks={[ { title: 'Duplicate Writes', description: 'Learn about making write operations resilient with duplicate writes handling.', link: './duplicate-writes', id: './duplicate-writes', }, { title: 'Update relationship tuples in SDK', description: 'Learn about how to update relationship tuples in SDK.', link: '../getting-started/update-tuples', id: '../getting-started/update-tuples', }, { title: '{ProductName} API', description: 'Details on the write API in the {ProductName} reference guide.', link: '/api/service#Relationship%20Tuples/Write', }, ]} />