Skip to main content

Transactional Writes

UsingOpenFGA, you can update multiple relationship tuples in a single transaction.

When to use

Updating multiple relationship tuples can keep your system state consistent.

Before you start

Familiarize yourself with basic OpenFGA Concepts before completing this guide.

In the following authorization model, there is type called tweet that can have a reader. There is another type called user that can have a follower and followed_by relationship.

model
schema 1.1

type tweet
relations
define viewer: [user, user:*, user#follower]

type user
relations
define follower: [user]
define followed_by: [user]

In addition:

Direct access

Creating an authorization model and a relationship tuple grants a user access to an object. To learn more, read about Direct Access.

Modeling public access

The following example uses public access. To learn more, read about Public Access.

OpenFGA concepts

  • A Type: a class of objects that have similar characteristics
  • A User: an entity in the system that can be related to an object
  • A Relation: 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 Relation: 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 Relationship Tuple: a group stored in OpenFGA that consists of a user, a relation, and an object

Step by step

01. Add and remove relationship tuples in the same transaction

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:

Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');

// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});

await fgaClient.write({
writes: [
{"user":"user:*","relation":"viewer","object":"tweet:1"}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});

Deleting the previous tuple converts this tweet to private:

Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');

// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});

await fgaClient.write({
deletes: [
{ user: 'user:*', relation: 'viewer', object: 'tweet:1'}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});

By removing the tuple, we made the tweet visible to no-one, which may not be what we want.

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.

Initialize the SDK
// ApiTokenIssuer, ApiAudience, ClientId and ClientSecret are optional.
// import the SDK
const { OpenFgaClient } = require('@openfga/sdk');

// Initialize the SDK with no auth - see "How to setup SDK client" for more options
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL, // required, e.g. https://api.fga.example
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional, can be overridden per request
});

await fgaClient.write({
writes: [
// Anne's followers can view tweet:1
{"_description":"Anne's followers can view tweet:1","user":"user:anne#follower","relation":"viewer","object":"tweet:1"}
],
deletes: [
// tweet:1 is no longer viewable by everyone (*)
{ user: 'user:*', relation: 'viewer', object: 'tweet:1'}
],
}, {
authorization_model_id: "01HVMMBCMGZNT3SED4Z17ECXCA"
});

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:

[// Anne is a follower of Becky
{
"_description": "Anne is a follower of Becky",
"user": "user:anne",
"relation": "follower",
"object": "user:becky"
}// Becky is followed by Anne
{
"_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 OpenFGA 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.

Update relationship tuples in SDK

Learn about how to update relationship tuples in SDK.

OpenFGA API

Details on the write API in the OpenFGA reference guide.