Add or delete tuples
The Write API transactionally updates the tuples for a certain store. Tuples and type definitions allow OpenFGA to determine whether a relationship exists between an object and an user.
In the body, writes adds new tuples and deletes removes existing tuples. When deleting a tuple, any condition specified with it is ignored.
The API is not idempotent by default: if, later on, you try to add the same tuple key (even if the condition is different), or if you try to delete a non-existing tuple, it throws an error.
To allow writes when an identical tuple already exists in the database, set "on_duplicate": "ignore" on the writes object.
To allow deletes when a tuple was already removed from the database, set "on_missing": "ignore" on the deletes object.
If a Write request contains both idempotent (ignore) and non-idempotent (error) operations, the most restrictive action (error) takes precedence. If a condition fails for a sub-request with an error flag, the entire transaction will be rolled back. This gives developers explicit control over the atomicity of the requests.
The API does not allow you to write tuples such as document:2021-budget#viewer@document:2021-budget#viewer, because they are implicit.
An authorization_model_id may be specified in the body. If it is, model ID is used to assert that each written tuple (not deleted) is valid for the model specified. If it is not specified, the latest authorization model ID is used.
Example
Adding relationships
To add user:anne as a writer for document:2021-budget, call write API with the following:
{
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "writer",
"object": "document:2021-budget"
}
],
"on_duplicate": "ignore"
},
"authorization_model_id": "01G50QVV17PECNVAHX1GG4Y5NC"
}
Removing relationships
To remove user:bob as a reader for document:2021-budget, call write API with the following:
{
"deletes": {
"tuple_keys": [
{
"user": "user:bob",
"relation": "reader",
"object": "document:2021-budget"
}
],
"on_missing": "ignore"
}
}
const { OpenFgaClient, ConsistencyPreference, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes } = require('@openfga/sdk');
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional; requests can override this.
});
async function main() {
const body = {
"writes": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
};
const options = {
};
const response = await fgaClient.write(body, options);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});package main
import (
"context"
"os"
. "github.com/openfga/go-sdk/client"
)
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional; requests can override this.
})
if err != nil {
panic(err)
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
ClientTupleKey{
User: "user:anne",
Relation: "reader",
Object: "document:budget",
},
},
}
options := ClientWriteOptions{
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()
if err != nil {
panic(err)
}
_ = data
}using System.Collections.Generic;
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
var fgaClient = new OpenFgaClient(new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional; requests can override this.
});
var body = new ClientWriteRequest {
Writes = new List<ClientTupleKey> {
new ClientTupleKey {
User = "user:anne",
Relation = "reader",
Object = "document:budget",
},
},
};
var options = new ClientWriteOptions {
};
var response = await fgaClient.Write(body, options);import asyncio
import os
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.client.models import ClientWriteRequest, ClientTuple
from openfga_sdk.models import RelationshipCondition
from openfga_sdk.client.models import ConflictOptions, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes
async def main():
configuration = ClientConfiguration(
api_url=os.environ.get("FGA_API_URL"),
store_id=os.environ.get("FGA_STORE_ID"),
authorization_model_id=os.environ.get("FGA_MODEL_ID"), # Optional; requests can override this.
)
async with OpenFgaClient(configuration) as fga_client:
body = ClientWriteRequest(
writes=[
ClientTuple(
user="user:anne",
relation="reader",
object="document:budget",
)
],
)
options = {
}
response = await fga_client.write(body, options)
asyncio.run(main())import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.*;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.*;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL"))
.storeId(System.getenv("FGA_STORE_ID"))
.authorizationModelId(System.getenv("FGA_MODEL_ID")); // Optional; requests can override this.
var fgaClient = new OpenFgaClient(config);
var body = new ClientWriteRequest()
.writes(java.util.Arrays.asList(new ClientTupleKey()
.user("user:anne")
.relation("reader")
._object("document:budget")));
var options = new ClientWriteOptions();
var response = fgaClient.write(body, options).get();
}
}# Set FGA_API_URL to the URL of your OpenFGA server.
# Set FGA_STORE_ID to your store ID.
# These examples use a server with authentication disabled.
# For authenticated servers, see /docs/getting-started/setup-sdk-client.
curl -X POST "$FGA_API_URL/stores/$FGA_STORE_ID/write" \
-H "content-type: application/json" \
-d '{
"authorization_model_id": "'"$FGA_MODEL_ID"'",
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
}
}'{}{
"code": "validation_error",
"message": "Generic validation error"
}{
"code": "unauthenticated",
"message": "unauthenticated"
}{
"code": "forbidden",
"message": "the principal is not authorized to perform the action"
}{
"code": "undefined_endpoint",
"message": "Endpoint not enabled"
}{
"code": "10",
"message": "transaction conflict"
}{
"code": "throttled_timeout_error",
"message": "timeout due to throttling on complex request"
}{
"code": "internal_error",
"message": "Internal Server Error"
}Was this page helpful?
const { OpenFgaClient, ConsistencyPreference, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes } = require('@openfga/sdk');
const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID, // Optional; requests can override this.
});
async function main() {
const body = {
"writes": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
};
const options = {
};
const response = await fgaClient.write(body, options);
}
main().catch((error) => {
console.error(error);
process.exitCode = 1;
});package main
import (
"context"
"os"
. "github.com/openfga/go-sdk/client"
)
func main() {
fgaClient, err := NewSdkClient(&ClientConfiguration{
ApiUrl: os.Getenv("FGA_API_URL"),
StoreId: os.Getenv("FGA_STORE_ID"),
AuthorizationModelId: os.Getenv("FGA_MODEL_ID"), // Optional; requests can override this.
})
if err != nil {
panic(err)
}
body := ClientWriteRequest{
Writes: []ClientTupleKey{
ClientTupleKey{
User: "user:anne",
Relation: "reader",
Object: "document:budget",
},
},
}
options := ClientWriteOptions{
}
data, err := fgaClient.Write(context.Background()).Body(body).Options(options).Execute()
if err != nil {
panic(err)
}
_ = data
}using System.Collections.Generic;
using OpenFga.Sdk.Client;
using OpenFga.Sdk.Client.Model;
using OpenFga.Sdk.Model;
using Environment = System.Environment;
var fgaClient = new OpenFgaClient(new ClientConfiguration() {
ApiUrl = Environment.GetEnvironmentVariable("FGA_API_URL"),
StoreId = Environment.GetEnvironmentVariable("FGA_STORE_ID"),
AuthorizationModelId = Environment.GetEnvironmentVariable("FGA_MODEL_ID"), // Optional; requests can override this.
});
var body = new ClientWriteRequest {
Writes = new List<ClientTupleKey> {
new ClientTupleKey {
User = "user:anne",
Relation = "reader",
Object = "document:budget",
},
},
};
var options = new ClientWriteOptions {
};
var response = await fgaClient.Write(body, options);import asyncio
import os
from openfga_sdk.client import OpenFgaClient, ClientConfiguration
from openfga_sdk.client.models import ClientWriteRequest, ClientTuple
from openfga_sdk.models import RelationshipCondition
from openfga_sdk.client.models import ConflictOptions, ClientWriteRequestOnDuplicateWrites, ClientWriteRequestOnMissingDeletes
async def main():
configuration = ClientConfiguration(
api_url=os.environ.get("FGA_API_URL"),
store_id=os.environ.get("FGA_STORE_ID"),
authorization_model_id=os.environ.get("FGA_MODEL_ID"), # Optional; requests can override this.
)
async with OpenFgaClient(configuration) as fga_client:
body = ClientWriteRequest(
writes=[
ClientTuple(
user="user:anne",
relation="reader",
object="document:budget",
)
],
)
options = {
}
response = await fga_client.write(body, options)
asyncio.run(main())import dev.openfga.sdk.api.client.OpenFgaClient;
import dev.openfga.sdk.api.configuration.ClientConfiguration;
import dev.openfga.sdk.api.configuration.*;
import dev.openfga.sdk.api.client.model.*;
import dev.openfga.sdk.api.model.*;
import java.util.List;
import java.util.Map;
import java.util.ArrayList;
public class Example {
public static void main(String[] args) throws Exception {
var config = new ClientConfiguration()
.apiUrl(System.getenv("FGA_API_URL"))
.storeId(System.getenv("FGA_STORE_ID"))
.authorizationModelId(System.getenv("FGA_MODEL_ID")); // Optional; requests can override this.
var fgaClient = new OpenFgaClient(config);
var body = new ClientWriteRequest()
.writes(java.util.Arrays.asList(new ClientTupleKey()
.user("user:anne")
.relation("reader")
._object("document:budget")));
var options = new ClientWriteOptions();
var response = fgaClient.write(body, options).get();
}
}# Set FGA_API_URL to the URL of your OpenFGA server.
# Set FGA_STORE_ID to your store ID.
# These examples use a server with authentication disabled.
# For authenticated servers, see /docs/getting-started/setup-sdk-client.
curl -X POST "$FGA_API_URL/stores/$FGA_STORE_ID/write" \
-H "content-type: application/json" \
-d '{
"authorization_model_id": "'"$FGA_MODEL_ID"'",
"writes": {
"tuple_keys": [
{
"user": "user:anne",
"relation": "reader",
"object": "document:budget"
}
]
}
}'{}{
"code": "validation_error",
"message": "Generic validation error"
}{
"code": "unauthenticated",
"message": "unauthenticated"
}{
"code": "forbidden",
"message": "the principal is not authorized to perform the action"
}{
"code": "undefined_endpoint",
"message": "Endpoint not enabled"
}{
"code": "10",
"message": "transaction conflict"
}{
"code": "throttled_timeout_error",
"message": "timeout due to throttling on complex request"
}{
"code": "internal_error",
"message": "Internal Server Error"
}