openfga

package
v0.110.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 26 Imported by: 0

Documentation

Overview

Package openfga is an idiomatic Go client for the OpenFGA HTTP API.

Construction

Build a client with NewClient and one or more Option values:

c, err := openfga.NewClient("https://api.fga.example",
	openfga.WithStoreID("01H..."),
	openfga.WithAPIToken("secret"))

NewClient never reads the environment. To configure from FGA_* variables, use NewClientFromEnv (or EnvOptions to merge env with explicit options).

Services

API calls are grouped into service handles on the Client:

c.Stores               // create/list/get/delete stores
c.AuthorizationModels  // write/read authorization models
c.Tuples               // write/read relationship tuples and changes
c.Relationships        // check, batch-check, expand, list-objects, list-users
c.Assertions           // read/write test assertions

Methods return (result, error), or just error for write-only calls. To reach the raw HTTP response (status, headers, request ID), pass the OnResponse request option, which receives the *Response after the body is decoded. For cross-cutting observation of every request, use WithRequestObserver; for full manual control, use NewRequest and Do.

Common calls

Relationships.Allowed is the shortcut for the most common query:

ok, err := c.Relationships.Allowed(ctx, "user:anne", "reader", "document:budget")

NewTupleKey and NewCheckRequest build the request structs with less ceremony.

Authentication

Pass exactly one authentication option, or none for an unauthenticated client: WithAPIToken, WithClientCredentials, WithPrivateKeyJWT, or WithTokenSource (any oauth2.TokenSource, e.g. Vault or workload identity).

Pagination

Range-over-func iterators page transparently; the second loop value is an error you must check:

for store, err := range c.Stores.All(ctx, nil) {
	if err != nil { return err }
	// use store
}

Stores.All, AuthorizationModels.All, Tuples.ReadAll, and Tuples.ChangesAll follow this shape; the underlying List/Read methods expose manual cursors.

Errors

Non-2xx responses become typed errors reachable with errors.As: *ValidationError (400), *AuthenticationError (401/403), *NotFoundError (404), *RateLimitError (429, with RetryAfter), and *InternalError (5xx). All embed *ErrorResponse, whose Code holds the OpenFGA error code (see the Code* constants) and whose RequestID reports the server correlation ID.

Options

Client-wide options and per-call RequestOptions overlap by design; per-call wins. The pairs are WithStoreID/WithStore, WithAuthorizationModelID/WithAuthorizationModel, and WithDefaultConsistency/WithConsistency.

Transport and extensibility

The client owns only an *http.Client; authentication, retries, and static headers are layered as composable http.RoundTripper transports. Add tracing, metrics, or a custom dialer beneath that chain with WithBaseTransport (it also carries out-of-band token fetches), observe each attempt with WithRequestObserver, or replace the whole stack with WithHTTPClient.

Example (ErrorHandling)

Example_errorHandling matches the typed errors the client returns. All embed *ErrorResponse, so errors.As reaches both the specific type and the base.

package main

import (
	"context"
	"errors"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	_, err = client.Relationships.Allowed(
		context.Background(), "user:anne", "reader", "document:budget")

	var rl *openfga.RateLimitError
	var notFound *openfga.NotFoundError
	var apiErr *openfga.ErrorResponse
	switch {
	case errors.As(err, &rl):
		fmt.Println("rate limited; retry after", rl.RetryAfter)
	case errors.As(err, &notFound):
		fmt.Println("not found:", notFound.RequestID())
	case errors.As(err, &apiErr):
		fmt.Println("api error code:", apiErr.Code)
	case err != nil:
		fmt.Println("transport error:", err)
	}
}

Index

Examples

Constants

View Source
const (
	CodeValidationError            = "validation_error"
	CodeInvalidAuthorizationModel  = "invalid_authorization_model"
	CodeTypeNotFound               = "type_not_found"
	CodeRelationNotFound           = "relation_not_found"
	CodeStoreIDInvalidLength       = "store_id_invalid_length"
	CodeAuthorizationModelNotFound = "authorization_model_not_found"
	CodeInternalError              = "internal_error"
)

OpenFGA error codes carried in ErrorResponse.Code. This is not the full set the server may return; match on ErrorResponse.Code directly for others. See https://openfga.dev/api/service for the authoritative list.

Variables

This section is empty.

Functions

This section is empty.

Types

type Assertion

type Assertion struct {
	TupleKey         CheckRequestTupleKey `json:"tuple_key"`
	Expectation      bool                 `json:"expectation"`
	ContextualTuples []TupleKey           `json:"contextual_tuples,omitempty"`
	Context          map[string]any       `json:"context,omitempty"`
}

Assertion is a single test case for an authorization model: a tuple key, the expected Check outcome, and optional contextual tuples or condition context.

type AssertionsService

type AssertionsService service

AssertionsService groups the assertion endpoints (write/read).

func (*AssertionsService) Read

Read retrieves the assertions for the given authorization model ID. It issues a GET request to /stores/{store}/assertions/{modelID}.

func (*AssertionsService) Write

func (s *AssertionsService) Write(ctx context.Context, modelID string, req *WriteAssertionsRequest, opts ...RequestOption) error

Write replaces the assertions for the given authorization model ID. It issues a PUT request to /stores/{store}/assertions/{modelID}.

Example

ExampleAssertionsService_Write records assertions that pin the expected Check outcome for a model, so a model change that breaks them is caught early.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	modelID := "01HXAAAAAAAAAAAAAAAAAAAAAA"
	err = client.Assertions.Write(context.Background(), modelID, &openfga.WriteAssertionsRequest{
		Assertions: []openfga.Assertion{
			{
				TupleKey:    openfga.CheckRequestTupleKey{User: "user:anne", Relation: "viewer", Object: "document:budget"},
				Expectation: true,
			},
			{
				TupleKey:    openfga.CheckRequestTupleKey{User: "user:bob", Relation: "owner", Object: "document:budget"},
				Expectation: false,
			},
		},
	})
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("assertions written")
}

type AuthenticationError

type AuthenticationError struct{ *ErrorResponse }

AuthenticationError is returned for HTTP 401 and 403 responses.

func (*AuthenticationError) Unwrap

func (e *AuthenticationError) Unwrap() error

Unwrap allows errors.As to reach the embedded *ErrorResponse.

type AuthorizationModel

type AuthorizationModel struct {
	ID              string               `json:"id"`
	SchemaVersion   string               `json:"schema_version"`
	TypeDefinitions []TypeDefinition     `json:"type_definitions"`
	Conditions      map[string]Condition `json:"conditions,omitempty"`
}

AuthorizationModel represents an OpenFGA authorization model.

type AuthorizationModelsService

type AuthorizationModelsService service

AuthorizationModelsService groups the authorization-model endpoints (write/list/get/read-latest).

func (*AuthorizationModelsService) All

All iterates every authorization model across pages. It copies opts so the caller's struct is never mutated.

func (*AuthorizationModelsService) Get

Get retrieves a single authorization model by ID.

func (*AuthorizationModelsService) List

List returns a page of authorization models for the store.

func (*AuthorizationModelsService) ReadLatest

ReadLatest returns the most recently created authorization model by fetching one page of size 1. It returns an error if no models exist in the store.

func (*AuthorizationModelsService) Write

Write creates a new authorization model in the store and returns its ID.

Example

ExampleAuthorizationModelsService_Write authors a model in Go with the typed builder helpers, writes it, and adopts the returned model ID as the client default. The builders map to the DSL: This is `[...]`, ComputedUserset is a bare relation, and Union is `or`.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	req := &openfga.WriteAuthorizationModelRequest{
		SchemaVersion: "1.1",
		TypeDefinitions: []openfga.TypeDefinition{
			{Type: "user"},
			{
				Type: "document",
				Relations: map[string]openfga.Userset{
					"owner":  openfga.This(),
					"editor": openfga.Union(openfga.This(), openfga.ComputedUserset("owner")),
					"viewer": openfga.Union(openfga.This(), openfga.ComputedUserset("editor")),
				},
				Metadata: &openfga.Metadata{
					Relations: map[string]openfga.RelationMetadata{
						"owner":  {DirectlyRelatedUserTypes: []openfga.RelationReference{openfga.DirectType("user")}},
						"editor": {DirectlyRelatedUserTypes: []openfga.RelationReference{openfga.DirectType("user")}},
						"viewer": {DirectlyRelatedUserTypes: []openfga.RelationReference{openfga.DirectType("user")}},
					},
				},
			},
		},
	}

	resp, err := client.AuthorizationModels.Write(context.Background(), req)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	_ = client.SetAuthorizationModelID(resp.AuthorizationModelID)
	fmt.Println("wrote model:", resp.AuthorizationModelID)
}

type BatchCheckItem

type BatchCheckItem struct {
	TupleKey         CheckRequestTupleKey `json:"tuple_key"`
	ContextualTuples *ContextualTupleKeys `json:"contextual_tuples,omitempty"`
	Context          map[string]any       `json:"context,omitempty"`
	CorrelationID    string               `json:"correlation_id,omitempty"`
}

BatchCheckItem is a single check within a BatchCheckRequest.

type BatchCheckRequest

type BatchCheckRequest struct {
	Checks               []BatchCheckItem      `json:"checks"`
	AuthorizationModelID string                `json:"authorization_model_id,omitempty"`
	Consistency          ConsistencyPreference `json:"consistency,omitempty"`
}

BatchCheckRequest is the body for Relationships.BatchCheck.

type BatchCheckResponse

type BatchCheckResponse struct {
	Result map[string]BatchCheckSingleResult `json:"result"`
}

BatchCheckResponse is returned by Relationships.BatchCheck. Results are keyed by the CorrelationID supplied in each BatchCheckItem.

type BatchCheckSingleResult

type BatchCheckSingleResult struct {
	Allowed bool           `json:"allowed"`
	Error   map[string]any `json:"error,omitempty"`
}

BatchCheckSingleResult holds the outcome of one check within a batch.

type CheckRequest

type CheckRequest struct {
	TupleKey             CheckRequestTupleKey  `json:"tuple_key"`
	ContextualTuples     *ContextualTupleKeys  `json:"contextual_tuples,omitempty"`
	AuthorizationModelID string                `json:"authorization_model_id,omitempty"`
	Context              map[string]any        `json:"context,omitempty"`
	Consistency          ConsistencyPreference `json:"consistency,omitempty"`
}

CheckRequest is the body for Relationships.Check.

func NewCheckRequest

func NewCheckRequest(user, relation, object string) *CheckRequest

NewCheckRequest builds a CheckRequest for the common case of checking whether user has relation on object. Set the remaining fields (ContextualTuples, Context, AuthorizationModelID, Consistency) on the result as needed.

type CheckRequestTupleKey

type CheckRequestTupleKey struct {
	User     string `json:"user"`
	Relation string `json:"relation"`
	Object   string `json:"object"`
}

CheckRequestTupleKey identifies the relationship to check. Unlike TupleKey, it carries no condition field. It is also reused by AssertionsService.

type CheckResponse

type CheckResponse struct {
	Allowed    bool   `json:"allowed"`
	Resolution string `json:"resolution,omitempty"`
}

CheckResponse is returned by Relationships.Check.

type Client

type Client struct {
	Stores              *StoresService
	AuthorizationModels *AuthorizationModelsService
	Tuples              *TuplesService
	Relationships       *RelationshipsService
	Assertions          *AssertionsService
	// contains filtered or unexported fields
}

Client is an OpenFGA API client. Construct it with NewClient.

func NewClient

func NewClient(apiURL string, opts ...Option) (*Client, error)

NewClient creates a client targeting apiURL (e.g. "https://api.fga.example"). Construction is explicit: apiURL and opts are the only inputs, applied in order so later opts win. It does not read the environment — use NewClientFromEnv or EnvOptions to opt into FGA_* configuration.

Example

ExampleNewClient shows how to construct a Client with a store ID and API token. No Output comment is present, so this is a compile-only example.

package main

import (
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
		openfga.WithAPIToken("my-api-token"),
	)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println(client.BaseURL())
}

func NewClientFromEnv

func NewClientFromEnv(opts ...Option) (*Client, error)

NewClientFromEnv creates a client from FGA_* environment variables, with opts overriding the environment-derived configuration. It is the explicit opt-in for environment configuration; NewClient alone never reads the environment.

Example

ExampleNewClientFromEnv builds a client from FGA_* environment variables, with explicit options overriding the environment.

package main

import (
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClientFromEnv(
		openfga.WithUserAgent("my-app/1.0"),
	)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println(client.BaseURL())
}

func (*Client) AuthorizationModelID

func (c *Client) AuthorizationModelID() string

AuthorizationModelID returns the client's default authorization model ID (empty if unset).

func (*Client) BareDo

func (c *Client) BareDo(req *http.Request) (*Response, error)

BareDo executes a request through the transport chain, classifies errors, and returns the wrapped response without decoding the body.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the API base URL the client targets.

func (*Client) DefaultConsistency

func (c *Client) DefaultConsistency() ConsistencyPreference

DefaultConsistency returns the client's default read consistency.

func (*Client) Do

func (c *Client) Do(req *http.Request, v any) (*Response, error)

Do executes a request and decodes a 2xx JSON body into v (which may be nil). If v implements continuationTokener, its token is lifted onto Response.

func (*Client) NewRequest

func (c *Client) NewRequest(ctx context.Context, method, path string, body any, opts ...RequestOption) (*http.Request, error)

NewRequest builds an *http.Request against the client base URL. It is public so callers can hit arbitrary endpoints while reusing the configured transport.

Example

ExampleClient_NewRequest demonstrates the arbitrary-call escape hatch for endpoints not yet covered by the typed service methods. No Output comment is present, so this is a compile-only example.

package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
		openfga.WithAPIToken("my-api-token"),
	)
	if err != nil {
		panic(err)
	}

	req, err := client.NewRequest(context.Background(), http.MethodGet, "/stores", nil)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("method:", req.Method)
}

func (*Client) SetAuthorizationModelID

func (c *Client) SetAuthorizationModelID(id string) error

SetAuthorizationModelID updates the client's default authorization model ID, validating it as a ULID (an empty string clears it). Same concurrency caveat as SetStoreID; use the per-call WithAuthorizationModel option for concurrent overrides.

func (*Client) SetDefaultConsistency

func (c *Client) SetDefaultConsistency(cons ConsistencyPreference)

SetDefaultConsistency updates the client's default read consistency. Same concurrency caveat as SetStoreID; use the per-call WithConsistency option for concurrent overrides.

func (*Client) SetStoreID

func (c *Client) SetStoreID(id string) error

SetStoreID updates the client's default store ID, validating it as a ULID (an empty string clears it). Intended for reconfiguring a client between requests; it is not safe to call concurrently with in-flight requests. Use the per-call WithStore option for concurrent overrides.

func (*Client) StoreID

func (c *Client) StoreID() string

StoreID returns the client's default store ID (empty if unset).

func (*Client) Transport

func (c *Client) Transport() http.RoundTripper

Transport returns the http.RoundTripper the client uses: the assembled retry/auth/header chain, or the transport of a client supplied via WithHTTPClient. It lets callers reuse the SDK's configured transport, for example to wrap the whole logical request (across retries) in a span.

type ClientCredentialsConfig

type ClientCredentialsConfig struct {
	TokenURL     string
	ClientID     string
	ClientSecret string
	Audience     string
	Scopes       []string
}

ClientCredentialsConfig configures the OAuth2 client-credentials grant.

type Condition

type Condition struct {
	Name       string                        `json:"name"`
	Expression string                        `json:"expression"`
	Parameters map[string]ConditionParamType `json:"parameters,omitempty"`
	Metadata   *ConditionMetadata            `json:"metadata,omitempty"`
}

Condition is an ABAC condition referenced by tuples and relation references.

type ConditionMetadata

type ConditionMetadata struct {
	Module     string      `json:"module,omitempty"`
	SourceInfo *SourceInfo `json:"source_info,omitempty"`
}

ConditionMetadata is optional source metadata attached to a condition.

type ConditionParamType

type ConditionParamType struct {
	TypeName     string               `json:"type_name"`
	GenericTypes []ConditionParamType `json:"generic_types,omitempty"`
}

ConditionParamType is the type of a condition parameter, e.g. TYPE_NAME_INT. GenericTypes carries the element type(s) for container types such as TYPE_NAME_LIST and TYPE_NAME_MAP.

type ConsistencyPreference

type ConsistencyPreference string

ConsistencyPreference controls read consistency for relationship queries.

const (
	ConsistencyUnspecified       ConsistencyPreference = "UNSPECIFIED"
	ConsistencyMinimizeLatency   ConsistencyPreference = "MINIMIZE_LATENCY"
	ConsistencyHigherConsistency ConsistencyPreference = "HIGHER_CONSISTENCY"
)

Consistency preferences accepted by the relationship query endpoints.

type ContextualTupleKeys

type ContextualTupleKeys struct {
	TupleKeys []TupleKey `json:"tuple_keys"`
}

ContextualTupleKeys is a set of tuple keys provided as context for a query. These tuples are treated as if they were already written to the store for the duration of the request.

type CreateStoreRequest

type CreateStoreRequest struct {
	Name string `json:"name"`
}

CreateStoreRequest is the body for Create.

type Difference

type Difference struct {
	Base     Userset `json:"base"`
	Subtract Userset `json:"subtract"`
}

Difference is Base minus Subtract ("A but not B" in the DSL).

type DirectUserset

type DirectUserset struct{}

DirectUserset marks a relation as directly assignable ("this" in the DSL, the `[...]` type restriction). It serializes as an empty JSON object.

type ErrorResponse

type ErrorResponse struct {
	Response *http.Response `json:"-"`
	Code     string         `json:"code"`
	Message  string         `json:"message"`
}

ErrorResponse is the base error returned for any non-2xx API response.

func (*ErrorResponse) Error

func (e *ErrorResponse) Error() string

func (*ErrorResponse) RequestID

func (e *ErrorResponse) RequestID() string

RequestID returns the OpenFGA request correlation ID from the response headers, or "" if absent. Quote it when reporting a server-side error.

func (*ErrorResponse) StatusCode

func (e *ErrorResponse) StatusCode() int

StatusCode returns the HTTP status code of the response that produced this error, or 0 if the error carries no response.

type ExpandRequest

type ExpandRequest struct {
	TupleKey             CheckRequestTupleKey  `json:"tuple_key"`
	ContextualTuples     *ContextualTupleKeys  `json:"contextual_tuples,omitempty"`
	AuthorizationModelID string                `json:"authorization_model_id,omitempty"`
	Context              map[string]any        `json:"context,omitempty"`
	Consistency          ConsistencyPreference `json:"consistency,omitempty"`
}

ExpandRequest is the body for Relationships.Expand.

type ExpandResponse

type ExpandResponse struct {
	Tree map[string]any `json:"tree"`
}

ExpandResponse is returned by Relationships.Expand. The tree is returned as an untyped map to accommodate the recursive, schema-version-dependent shape.

type FGAObject

type FGAObject struct {
	Type string `json:"type"`
	ID   string `json:"id"`
}

FGAObject is a concrete object reference, e.g. {Type: "user", ID: "anne"}.

type FGAObjectRelation

type FGAObjectRelation struct {
	Object   string `json:"object,omitempty"`
	Relation string `json:"relation,omitempty"`
}

FGAObjectRelation identifies an object and an optional relation, used as the target in ListUsersRequest. The Object is given in the convenient "type:id" string form; it is serialized to OpenFGA's structured {type, id} object on the wire (see MarshalJSON).

func (FGAObjectRelation) MarshalJSON

func (o FGAObjectRelation) MarshalJSON() ([]byte, error)

MarshalJSON encodes the object in the structure OpenFGA's ListUsers endpoint expects: a nested {"type": ..., "id": ...} object split from the "type:id" string form. The optional relation is included only when set.

func (*FGAObjectRelation) UnmarshalJSON

func (o *FGAObjectRelation) UnmarshalJSON(data []byte) error

UnmarshalJSON is the inverse of MarshalJSON: it accepts the structured {type, id, relation} object and rebuilds the "type:id" string form. A bare JSON string is also accepted for backward compatibility.

type InternalError

type InternalError struct{ *ErrorResponse }

InternalError is returned for HTTP 5xx responses.

func (*InternalError) Unwrap

func (e *InternalError) Unwrap() error

Unwrap allows errors.As to reach the embedded *ErrorResponse.

type ListAuthorizationModelsResponse

type ListAuthorizationModelsResponse struct {
	AuthorizationModels []AuthorizationModel `json:"authorization_models"`
	ContinuationToken   string               `json:"continuation_token"`
}

ListAuthorizationModelsResponse is a page of authorization models returned by List.

type ListModelsOptions

type ListModelsOptions struct {
	PageSize          int
	ContinuationToken string
}

ListModelsOptions controls pagination for the List method.

type ListObjectsRequest

type ListObjectsRequest struct {
	Type                 string                `json:"type"`
	Relation             string                `json:"relation"`
	User                 string                `json:"user"`
	ContextualTuples     *ContextualTupleKeys  `json:"contextual_tuples,omitempty"`
	AuthorizationModelID string                `json:"authorization_model_id,omitempty"`
	Context              map[string]any        `json:"context,omitempty"`
	Consistency          ConsistencyPreference `json:"consistency,omitempty"`
}

ListObjectsRequest is the body for Relationships.ListObjects.

type ListObjectsResponse

type ListObjectsResponse struct {
	Objects []string `json:"objects"`
}

ListObjectsResponse is returned by Relationships.ListObjects.

type ListRelationsRequest

type ListRelationsRequest struct {
	User                 string
	Object               string
	Relations            []string
	ContextualTuples     *ContextualTupleKeys
	Context              map[string]any
	AuthorizationModelID string
	Consistency          ConsistencyPreference
}

ListRelationsRequest is the input to Relationships.ListRelations. It asks which of the candidate Relations the User has on the Object.

type ListStoresOptions

type ListStoresOptions struct {
	PageSize          int
	ContinuationToken string
	Name              string // filter to stores with this exact name; optional
}

ListStoresOptions controls List pagination and filtering.

type ListStoresResponse

type ListStoresResponse struct {
	Stores            []Store `json:"stores"`
	ContinuationToken string  `json:"continuation_token"`
}

ListStoresResponse is the List result page.

type ListUsersRequest

type ListUsersRequest struct {
	Object               FGAObjectRelation     `json:"object"`
	Relation             string                `json:"relation"`
	UserFilters          []UserTypeFilter      `json:"user_filters"`
	ContextualTuples     *ContextualTupleKeys  `json:"contextual_tuples,omitempty"`
	AuthorizationModelID string                `json:"authorization_model_id,omitempty"`
	Context              map[string]any        `json:"context,omitempty"`
	Consistency          ConsistencyPreference `json:"consistency,omitempty"`
}

ListUsersRequest is the body for Relationships.ListUsers.

type ListUsersResponse

type ListUsersResponse struct {
	Users []User `json:"users"`
}

ListUsersResponse is returned by Relationships.ListUsers.

type Metadata

type Metadata struct {
	Relations  map[string]RelationMetadata `json:"relations,omitempty"`
	Module     string                      `json:"module,omitempty"`
	SourceInfo *SourceInfo                 `json:"source_info,omitempty"`
}

Metadata carries per-relation typing information for a type definition.

type NotFoundError

type NotFoundError struct{ *ErrorResponse }

NotFoundError is returned for HTTP 404 responses.

func (*NotFoundError) Unwrap

func (e *NotFoundError) Unwrap() error

Unwrap allows errors.As to reach the embedded *ErrorResponse.

type ObjectRelation

type ObjectRelation struct {
	Object   string `json:"object,omitempty"`
	Relation string `json:"relation,omitempty"`
}

ObjectRelation references a relation, optionally on a specific object.

type OnDuplicate

type OnDuplicate string

OnDuplicate controls how the server handles a write whose tuple already exists. Requires OpenFGA >= 1.10. Empty means the server default ("error").

const (
	OnDuplicateError  OnDuplicate = "error"
	OnDuplicateIgnore OnDuplicate = "ignore"
)

OnDuplicate modes accepted on the Writes block.

type OnMissing

type OnMissing string

OnMissing controls how the server handles a delete whose tuple does not exist. Requires OpenFGA >= 1.10. Empty means the server default ("error").

const (
	OnMissingError  OnMissing = "error"
	OnMissingIgnore OnMissing = "ignore"
)

OnMissing modes accepted on the Deletes block.

type Option

type Option func(*Client)

Option configures a Client during NewClient.

func EnvOptions

func EnvOptions() ([]Option, error)

EnvOptions resolves FGA_* environment variables into client options for NewClient. Place them ahead of your own options so explicit settings win:

envOpts, err := openfga.EnvOptions()
client, err := openfga.NewClient("", append(envOpts, opts...)...)

func WithAPIToken

func WithAPIToken(token string) Option

WithAPIToken authenticates with a pre-shared key (Authorization: Bearer).

func WithAuthorizationModelID

func WithAuthorizationModelID(id string) Option

WithAuthorizationModelID sets the default authorization model ID used by all requests.

func WithBaseTransport

func WithBaseTransport(rt http.RoundTripper) Option

WithBaseTransport sets the innermost http.RoundTripper beneath the SDK's retry, auth, and header layers. Use it to add tracing, metrics, logging, or a custom dialer while keeping the SDK's auth and retries — for example otelhttp.NewTransport(nil) for per-attempt spans. It also becomes the base for out-of-band OAuth2 token fetches. Defaults to http.DefaultTransport. Ignored when WithHTTPClient supplies a full client.

func WithBaseURL

func WithBaseURL(raw string) Option

WithBaseURL overrides the API base URL (highest precedence).

func WithClientCredentials

func WithClientCredentials(cfg ClientCredentialsConfig) Option

WithClientCredentials authenticates via the OAuth2 client-credentials grant.

func WithDefaultConsistency

func WithDefaultConsistency(cons ConsistencyPreference) Option

WithDefaultConsistency sets the read consistency applied to all relationship query and tuple read requests. A per-call WithConsistency option overrides it.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient supplies a fully-configured *http.Client (escape hatch). When set, the SDK does NOT assemble its own transport chain, so WithAPIToken, WithClientCredentials, WithPrivateKeyJWT, WithHeaders, WithRetry, and WithBaseTransport have no effect — configure auth, headers, and retries on the supplied client's Transport yourself.

func WithHeaders

func WithHeaders(h http.Header) Option

WithHeaders adds static headers applied to every request.

func WithPrivateKeyJWT

func WithPrivateKeyJWT(cfg PrivateKeyJWTConfig) Option

WithPrivateKeyJWT authenticates using a signed JWT client assertion.

func WithRequestObserver

func WithRequestObserver(obs RequestObserver) Option

WithRequestObserver registers a callback invoked once per HTTP attempt. It is the lightweight alternative to a custom transport for logging, metrics, or debug output. Ignored when WithHTTPClient supplies a full client.

func WithRetry

func WithRetry(cfg RetryConfig) Option

WithRetry tunes retry behavior. Only the fields you set take effect; a zero-valued MaxAttempts, MinWait, MaxWait, or RetryableStatus falls back to its default (3 attempts, 1s–30s, {429}). This means a partial config such as

WithRetry(RetryConfig{MaxAttempts: 5})

keeps the default 429 retry set and the MaxWait ceiling instead of silently disabling them. Jitter and Retry-After honoring stay enabled; supply your own transport via WithBaseTransport if you need to turn them off. Add 5xx to RetryableStatus to opt those statuses in (note this retries non-idempotent writes on ambiguous 5xx). Transient network errors are always retried.

func WithStoreID

func WithStoreID(id string) Option

WithStoreID sets the default OpenFGA store ID used by all requests.

func WithTokenSource

func WithTokenSource(src oauth2.TokenSource) Option

WithTokenSource authenticates every request with a bearer token obtained from any oauth2.TokenSource, for credential sources beyond the built-in modes (e.g. Vault, workload identity, a pre-existing token source). The SDK caches tokens via oauth2.ReuseTokenSource and keeps its retry and header chain beneath the auth layer. Pass exactly one authentication option to NewClient.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent overrides the User-Agent header sent on every request.

func WithoutRetry

func WithoutRetry() Option

WithoutRetry disables retries entirely.

type PrivateKeyJWTConfig

type PrivateKeyJWTConfig struct {
	TokenURL      string
	ClientID      string
	Audience      string // assertion "aud" (usually the token endpoint/issuer)
	APIAudience   string // OpenFGA API audience requested in the grant
	Scopes        []string
	SigningKey    crypto.PrivateKey // *rsa.PrivateKey or *ecdsa.PrivateKey
	SigningMethod jwt.SigningMethod
	KeyID         string
}

PrivateKeyJWTConfig configures client-credentials with a signed JWT assertion.

type RateLimitError

type RateLimitError struct {
	*ErrorResponse
	RetryAfter time.Duration
}

RateLimitError is returned on HTTP 429.

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

Unwrap allows errors.As to reach the embedded *ErrorResponse.

type ReadAssertionsResponse

type ReadAssertionsResponse struct {
	AuthorizationModelID string      `json:"authorization_model_id"`
	Assertions           []Assertion `json:"assertions"`
}

ReadAssertionsResponse is returned by AssertionsService.Read.

type ReadChangesOptions

type ReadChangesOptions struct {
	Type              string
	PageSize          int
	ContinuationToken string
	StartTime         string // RFC3339; optional
}

ReadChangesOptions controls filtering and pagination for Tuples.ReadChanges.

type ReadChangesResponse

type ReadChangesResponse struct {
	Changes           []TupleChange `json:"changes"`
	ContinuationToken string        `json:"continuation_token"`
}

ReadChangesResponse is the result of a Tuples.ReadChanges call.

type ReadRequest

type ReadRequest struct {
	TupleKey          *ReadRequestTupleKey  `json:"tuple_key,omitempty"`
	PageSize          int                   `json:"page_size,omitempty"`
	ContinuationToken string                `json:"continuation_token,omitempty"`
	Consistency       ConsistencyPreference `json:"consistency,omitempty"`
}

ReadRequest is the body for Tuples.Read.

type ReadRequestTupleKey

type ReadRequestTupleKey struct {
	User     string `json:"user,omitempty"`
	Relation string `json:"relation,omitempty"`
	Object   string `json:"object,omitempty"`
}

ReadRequestTupleKey is a partial tuple key used as a filter in Read requests. All fields are optional; omit a field to match any value.

type ReadResponse

type ReadResponse struct {
	Tuples            []Tuple `json:"tuples"`
	ContinuationToken string  `json:"continuation_token"`
}

ReadResponse is the result of a Tuples.Read call.

type RelationMetadata

type RelationMetadata struct {
	DirectlyRelatedUserTypes []RelationReference `json:"directly_related_user_types,omitempty"`
	Module                   string              `json:"module,omitempty"`
	SourceInfo               *SourceInfo         `json:"source_info,omitempty"`
}

RelationMetadata lists the user types that may be directly assigned to a relation (the `[...]` restriction in the DSL).

type RelationReference

type RelationReference struct {
	Type      string    `json:"type"`
	Relation  string    `json:"relation,omitempty"`
	Wildcard  *Wildcard `json:"wildcard,omitempty"`
	Condition string    `json:"condition,omitempty"`
}

RelationReference is one allowed user type for a relation: a bare type (`user`), a userset (`group#member`, via Relation), a type-bound wildcard (`user:*`, via Wildcard), or any of these gated by a condition (via Condition).

func DirectType

func DirectType(typ string) RelationReference

DirectType returns a directly-related user type for a relation's metadata, e.g. DirectType("user") for `[user]`. Set Relation for a userset (`group#member`), Wildcard for `user:*`, or Condition to gate the reference.

type RelationshipCondition

type RelationshipCondition struct {
	Name    string         `json:"name"`
	Context map[string]any `json:"context,omitempty"`
}

RelationshipCondition is an optional ABAC condition attached to a tuple.

type RelationshipsService

type RelationshipsService service

RelationshipsService groups the query endpoints (check/batch-check/expand/list-objects/list-users).

func (*RelationshipsService) Allowed

func (s *RelationshipsService) Allowed(ctx context.Context, user, relation, object string, opts ...RequestOption) (bool, error)

Allowed is a convenience wrapper over Check for the common case: it reports whether user has relation on object, using the client's default authorization model and consistency. For contextual tuples, ABAC context, or a per-call model, build a CheckRequest and call Check.

Example

ExampleRelationshipsService_Allowed shows the shortcut for the most common query: does a user have a relation on an object?

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	ok, err := client.Relationships.Allowed(
		context.Background(), "user:anne", "reader", "document:budget")
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("allowed:", ok)
}

func (*RelationshipsService) BatchCheck

BatchCheck runs multiple relationship checks in a single request. Results in BatchCheckResponse.Result are keyed by the CorrelationID of each item; items that omit one get a generated ID (surfaced in the response), and duplicate caller-supplied IDs are rejected before the request, since they would collide in the result map.

func (*RelationshipsService) BatchCheckAll

BatchCheckAll runs many checks by splitting req.Checks into chunks of at most WithMaxChecksPerBatch (default 50, the server maximum) and issuing the native /batch-check requests concurrently (bounded by WithMaxParallel). Results from every chunk are merged into a single map keyed by correlation ID. Items with an empty CorrelationID get a generated one. Duplicate caller-supplied correlation IDs are rejected before any request, since the merged map would collide.

Chunk failures do not abort the whole call: results from every chunk that succeeded are still returned in Result, and the errors from the chunks that failed are combined (via errors.Join) into the returned error. A non-nil error therefore means "some checks are missing from Result", not "no results" — mirroring the per-item reporting of Tuples.WriteTuples. Setup failures (no store ID, a duplicate correlation ID, correlation-ID generation) still return a nil response and that error before any request is issued.

func (*RelationshipsService) Check

Check tests whether a user has a specific relation on an object. Pass OnResponse to observe the raw HTTP response.

Example

ExampleRelationshipsService_Check shows how to call the Check API. No Output comment is present, so this is a compile-only example.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
		openfga.WithAPIToken("my-api-token"),
	)
	if err != nil {
		panic(err)
	}

	resp, err := client.Relationships.Check(context.Background(), &openfga.CheckRequest{
		TupleKey: openfga.CheckRequestTupleKey{
			User:     "user:anne",
			Relation: "reader",
			Object:   "document:budget",
		},
	})
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("allowed:", resp.Allowed)
}

func (*RelationshipsService) Expand

Expand returns the userset tree that proves a relationship.

func (*RelationshipsService) ListObjects

ListObjects returns all objects of a given type that a user has a specific relation with.

func (*RelationshipsService) ListRelations

func (s *RelationshipsService) ListRelations(ctx context.Context, req *ListRelationsRequest, opts ...RequestOption) ([]string, error)

ListRelations reports which of req.Relations the user has on the object. It issues the checks through BatchCheckAll (one native /batch-check request per chunk, bounded by WithMaxParallel) and returns the allowed relations in the order they were supplied. Duplicate relations are rejected. Because it builds on the native batch-check endpoint, it requires OpenFGA >= 1.8.0.

func (*RelationshipsService) ListUsers

ListUsers returns all users who have a specific relation with a given object.

func (*RelationshipsService) StreamedListObjects

StreamedListObjects streams matching objects, decoding the NDJSON response lazily. The HTTP connection stays open until iteration ends or the caller breaks. Each yielded error value is non-nil only on failure; on success it is nil.

Example

ExampleRelationshipsService_StreamedListObjects ranges over the NDJSON streaming endpoint, receiving objects as they arrive instead of buffering a whole page. The second loop value is an error that must be checked.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	req := &openfga.ListObjectsRequest{
		Type:     "document",
		Relation: "viewer",
		User:     "user:anne",
	}
	for obj, err := range client.Relationships.StreamedListObjects(context.Background(), req) {
		if err != nil {
			fmt.Println("error:", err)
			return
		}
		fmt.Println(obj.Object)
	}
}

type RequestObserver

type RequestObserver func(req *http.Request, resp *http.Response, err error, elapsed time.Duration)

RequestObserver is invoked once per HTTP attempt, after the request has been fully decorated with auth and static headers and the response (or error) is available. The elapsed argument measures that single attempt; with retries enabled an observer fires once per attempt. Use it for logging, metrics, or debug tracing without implementing an http.RoundTripper. It must not modify req or resp, and must not read resp.Body (doing so would consume it).

type RequestOption

type RequestOption func(*requestConfig)

RequestOption customizes a single request.

func OnResponse

func OnResponse(fn func(*Response)) RequestOption

OnResponse registers a callback invoked with the raw *Response of a single call, after the body is decoded. Use it on the (result, error) surface to reach status, headers, the request ID, or the continuation token without threading the response through the return signature:

res, err := client.Relationships.Check(ctx, req,
	openfga.OnResponse(func(r *openfga.Response) {
		log.Println("request id:", r.RequestID())
	}))

It fires whenever a response is received — including on API errors, so the callback can inspect headers on a failure. The fan-out helpers (Tuples.WriteTuples, Tuples.DeleteTuples, Relationships.BatchCheckAll, Relationships.ListRelations) issue several requests and do not invoke it.

func WithAuthorizationModel

func WithAuthorizationModel(id string) RequestOption

WithAuthorizationModel overrides the authorization model ID for one call.

func WithConsistency

func WithConsistency(c ConsistencyPreference) RequestOption

WithConsistency overrides the read consistency for one query call.

func WithMaxChecksPerBatch

func WithMaxChecksPerBatch(n int) RequestOption

WithMaxChecksPerBatch sets how many checks go into each /batch-check request issued by Relationships.BatchCheckAll. Non-positive values fall back to the default (50, the server maximum). Other methods ignore it.

func WithMaxParallel

func WithMaxParallel(n int) RequestOption

WithMaxParallel caps the number of concurrent HTTP requests issued by Tuples.WriteTuples, Tuples.DeleteTuples, and Relationships.BatchCheckAll. Non-positive values fall back to the default (10). Other methods ignore it.

func WithMaxPerChunk

func WithMaxPerChunk(n int) RequestOption

WithMaxPerChunk sets how many tuples go into each non-transactional request issued by Tuples.WriteTuples / Tuples.DeleteTuples. Non-positive values fall back to the default (50). Ignored by other methods and when WithTransaction is set.

Each chunk is one server-side atomic /write, so if any tuple in a chunk fails, every tuple in that chunk is reported failed with the same error. Larger chunks mean fewer requests but coarser per-tuple attribution on failure; pass WithMaxPerChunk(1) for exact per-tuple results.

func WithOnDuplicate

func WithOnDuplicate(v OnDuplicate) RequestOption

WithOnDuplicate sets the on_duplicate conflict mode on the write requests issued by Tuples.WriteTuples. Other methods ignore it.

func WithOnMissing

func WithOnMissing(v OnMissing) RequestOption

WithOnMissing sets the on_missing conflict mode on the delete requests issued by Tuples.DeleteTuples. Other methods ignore it.

func WithRequestHeader

func WithRequestHeader(key, value string) RequestOption

WithRequestHeader sets a header on a single request.

func WithStore

func WithStore(storeID string) RequestOption

WithStore overrides the store ID for one call.

func WithTransaction

func WithTransaction() RequestOption

WithTransaction makes Tuples.WriteTuples / Tuples.DeleteTuples send a single transactional /write request instead of chunking. Other methods ignore it.

type Response

type Response struct {
	*http.Response
	// ContinuationToken is populated from the decoded body of paginated
	// endpoints; empty when there are no further pages.
	ContinuationToken string
}

Response wraps the raw *http.Response and adds OpenFGA pagination metadata.

func (*Response) QueryDuration

func (r *Response) QueryDuration() (time.Duration, bool)

QueryDuration returns the time the OpenFGA server reports it spent evaluating the query, parsed from the Fga-Query-Duration-Ms response header. This is the server-side evaluation cost only — it excludes network round-trip and any client-side retries, which the RequestObserver's elapsed argument measures. The bool is false when the header is absent or unparseable (e.g. on endpoints that do not report it), letting callers distinguish that from a genuine 0.

Reach it from the (result, error) surface via OnResponse:

res, err := client.Relationships.Check(ctx, req,
	openfga.OnResponse(func(r *openfga.Response) {
		if d, ok := r.QueryDuration(); ok {
			metrics.Observe("fga.query", d)
		}
	}))

func (*Response) RequestID

func (r *Response) RequestID() string

RequestID returns the OpenFGA request correlation ID from the response headers, or "" if absent. Quote it when reporting an issue to correlate with server logs.

type RetryConfig

type RetryConfig struct {
	MaxAttempts     int           // total attempts including the first; default 3
	MinWait         time.Duration // base backoff; default 1s
	MaxWait         time.Duration // backoff ceiling; default 30s
	RetryableStatus []int         // default {429}; add 5xx to opt in
	HonorRetryAfter bool          // default true
	Jitter          bool          // default true (equal jitter)
}

RetryConfig controls automatic retries. Defaults retry only HTTP 429 plus transient network failures (connection resets, refused dials, timeouts).

type SourceInfo

type SourceInfo struct {
	File string `json:"file,omitempty"`
}

SourceInfo is DSL source-position metadata the transformer may attach. It is preserved on round-trips; you do not set it when authoring a model by hand.

type Store

type Store struct {
	ID        string     `json:"id"`
	Name      string     `json:"name"`
	CreatedAt time.Time  `json:"created_at"`
	UpdatedAt time.Time  `json:"updated_at"`
	DeletedAt *time.Time `json:"deleted_at,omitempty"`
}

Store is an OpenFGA store.

type StoresService

type StoresService service

StoresService groups the store lifecycle endpoints (create/list/get/delete).

func (*StoresService) All

All iterates every store across pages, fetching lazily one page at a time. It copies opts so the caller's struct is never mutated. Iteration stops when the server returns an empty continuation token, when the caller breaks, or when the server returns an error (which is yielded as the second value).

Example

ExampleStoresService_All ranges over every store, paging transparently. The second loop value is an error that must be checked before using the store.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient("https://api.fga.example")
	if err != nil {
		panic(err)
	}

	for store, err := range client.Stores.All(context.Background(), nil) {
		if err != nil {
			fmt.Println("error:", err)
			return
		}
		fmt.Println(store.Name)
	}
}

func (*StoresService) Create

func (s *StoresService) Create(ctx context.Context, req *CreateStoreRequest, opts ...RequestOption) (*Store, error)

Create creates a new store.

func (*StoresService) Delete

func (s *StoresService) Delete(ctx context.Context, storeID string, opts ...RequestOption) error

Delete removes a store by ID.

func (*StoresService) Get

func (s *StoresService) Get(ctx context.Context, storeID string, opts ...RequestOption) (*Store, error)

Get retrieves a store by ID.

func (*StoresService) List

List returns a page of stores.

type StreamedListObjectsResponse

type StreamedListObjectsResponse struct {
	Object string `json:"object"`
}

StreamedListObjectsResponse is one NDJSON line of the streaming response. The server wraps each item under "result".

type Tuple

type Tuple struct {
	Key       TupleKey  `json:"key"`
	Timestamp time.Time `json:"timestamp"`
}

Tuple is a stored relationship triple with a server-assigned timestamp.

type TupleChange

type TupleChange struct {
	TupleKey  TupleKey  `json:"tuple_key"`
	Operation string    `json:"operation"` // TUPLE_OPERATION_WRITE | TUPLE_OPERATION_DELETE
	Timestamp time.Time `json:"timestamp"`
}

TupleChange describes a single write or delete event in the changelog.

type TupleKey

type TupleKey struct {
	User      string                 `json:"user"`
	Relation  string                 `json:"relation"`
	Object    string                 `json:"object"`
	Condition *RelationshipCondition `json:"condition,omitempty"`
}

TupleKey identifies a relationship triple, with an optional condition.

func NewTupleKey

func NewTupleKey(user, relation, object string) TupleKey

NewTupleKey builds a TupleKey from the three required fields, e.g. NewTupleKey("user:anne", "reader", "document:budget"). Set Condition on the result for ABAC.

type TupleResult

type TupleResult struct {
	TupleKey TupleKey
	Status   WriteStatus
	Err      error
}

TupleResult is the per-tuple outcome of Tuples.WriteTuples / DeleteTuples. Err is non-nil exactly when Status is WriteStatusFailure.

type TupleToUserset

type TupleToUserset struct {
	Tupleset        ObjectRelation `json:"tupleset"`
	ComputedUserset ObjectRelation `json:"computedUserset"`
}

TupleToUserset rewrites through the Tupleset relation to the ComputedUserset relation on the resolved objects ("X from Y" in the DSL).

type TuplesService

type TuplesService service

TuplesService groups the relationship-tuple endpoints (write/read/changes).

func (*TuplesService) ChangesAll

func (s *TuplesService) ChangesAll(ctx context.Context, opts *ReadChangesOptions, ropts ...RequestOption) iter.Seq2[TupleChange, error]

ChangesAll iterates every tuple change across pages until the feed is caught up. It copies opts so the caller's struct is never mutated.

Unlike the other paginated endpoints, the OpenFGA /changes endpoint keeps returning a non-empty continuation token even once there are no more changes, so that callers can resume the feed later. Stopping only on an empty token would therefore loop forever, re-requesting empty pages. To drain the feed exactly once we also stop when a page yields no changes, or when the server hands back the same token we just sent (no forward progress).

func (*TuplesService) DeleteTuples

func (s *TuplesService) DeleteTuples(ctx context.Context, keys []TupleKey, opts ...RequestOption) (*WriteTuplesResponse, error)

DeleteTuples deletes many tuples, chunking them into non-transactional /write requests issued in parallel. Use WithMaxPerChunk, WithMaxParallel, WithOnMissing, and WithTransaction to tune behavior.

func (*TuplesService) Read

func (s *TuplesService) Read(ctx context.Context, req *ReadRequest, opts ...RequestOption) (*ReadResponse, error)

Read returns a page of relationship tuples from the given store. If Consistency is unset in req, it is filled from the per-call WithConsistency option.

func (*TuplesService) ReadAll

func (s *TuplesService) ReadAll(ctx context.Context, req *ReadRequest, ropts ...RequestOption) iter.Seq2[Tuple, error]

ReadAll iterates every tuple matching req across pages. It copies req so the caller's struct is never mutated.

func (*TuplesService) ReadChanges

func (s *TuplesService) ReadChanges(ctx context.Context, opts *ReadChangesOptions, ropts ...RequestOption) (*ReadChangesResponse, error)

ReadChanges returns a page of changelog entries (tuple writes/deletes) for the given store. Filtering and pagination are controlled via opts. Parameters are sent as query-string parameters: type, page_size, continuation_token, start_time.

func (*TuplesService) Write

func (s *TuplesService) Write(ctx context.Context, req *WriteRequest, opts ...RequestOption) error

Write writes or deletes relationship tuples in the given store. If AuthorizationModelID is unset in req, it is filled from the client's configured model ID (or the per-call WithAuthorizationModel override).

func (*TuplesService) WriteTuples

func (s *TuplesService) WriteTuples(ctx context.Context, keys []TupleKey, opts ...RequestOption) (*WriteTuplesResponse, error)

WriteTuples writes many tuples, chunking them into non-transactional /write requests issued in parallel. Use WithMaxPerChunk, WithMaxParallel, WithOnDuplicate, and WithTransaction to tune behavior. The returned response carries a per-tuple result (order matches keys); the top-level error is non-nil only when no request could be issued.

Example

ExampleTuplesService_WriteTuples writes an arbitrarily large slice of tuples as parallel non-transactional chunks, then inspects the per-tuple outcome.

package main

import (
	"context"
	"fmt"

	"github.com/sergiught/go-openfga/openfga"
)

func main() {
	client, err := openfga.NewClient(
		"https://api.fga.example",
		openfga.WithStoreID("01ARZ3NDEKTSV4RRFFQ69G5FAV"),
	)
	if err != nil {
		panic(err)
	}

	keys := []openfga.TupleKey{
		openfga.NewTupleKey("user:anne", "reader", "document:budget"),
		openfga.NewTupleKey("user:bob", "editor", "document:roadmap"),
	}

	resp, err := client.Tuples.WriteTuples(context.Background(), keys)
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	for _, r := range resp.Writes {
		if r.Err != nil {
			fmt.Printf("failed %s: %v\n", r.TupleKey.Object, r.Err)
		}
	}
}

type TypeDefinition

type TypeDefinition struct {
	Type      string             `json:"type"`
	Relations map[string]Userset `json:"relations,omitempty"`
	Metadata  *Metadata          `json:"metadata,omitempty"`
}

TypeDefinition describes a single type within an authorization model. Relations maps each relation name to its rewrite rule (Userset); Metadata carries the directly-assignable user types for those relations.

type TypedWildcard

type TypedWildcard struct {
	Type string `json:"type"`
}

TypedWildcard matches every user of a type, e.g. user:* ({Type: "user"}).

type User

type User struct {
	Object   *FGAObject     `json:"object,omitempty"`
	Userset  *UsersetUser   `json:"userset,omitempty"`
	Wildcard *TypedWildcard `json:"wildcard,omitempty"`
}

User is one entry in a ListUsersResponse. Exactly one of Object, Userset, or Wildcard is non-nil, identifying a concrete object, a userset, or a type-bound wildcard (e.g. user:*) respectively.

type UserTypeFilter

type UserTypeFilter struct {
	Type     string `json:"type"`
	Relation string `json:"relation,omitempty"`
}

UserTypeFilter limits ListUsers results to a specific object type and optional relation.

type Userset

type Userset struct {
	This            *DirectUserset  `json:"this,omitempty"`
	ComputedUserset *ObjectRelation `json:"computedUserset,omitempty"`
	TupleToUserset  *TupleToUserset `json:"tupleToUserset,omitempty"`
	Union           *Usersets       `json:"union,omitempty"`
	Intersection    *Usersets       `json:"intersection,omitempty"`
	Difference      *Difference     `json:"difference,omitempty"`
}

Userset is a relation's rewrite rule. Exactly one field is set. Use the builder helpers (This, ComputedUserset, TupleTo, Union, Intersection, Exclusion) rather than populating the fields by hand.

func ComputedUserset

func ComputedUserset(relation string) Userset

ComputedUserset returns a rewrite to another relation on the same object (the DSL bare-relation reference, e.g. `owner`).

func Exclusion

func Exclusion(base, subtract Userset) Userset

Exclusion returns base minus subtract (the DSL `but not`). It builds a Difference.

func Intersection

func Intersection(children ...Userset) Userset

Intersection returns the intersection of the given rewrites (the DSL `and`).

func This

func This() Userset

This returns a directly-assignable rewrite (the DSL `[...]` restriction).

func TupleTo

func TupleTo(tupleset, computedRelation string) Userset

TupleTo returns a rewrite that follows the tupleset relation and then applies computedRelation on the resolved objects (the DSL `computedRelation from tupleset`). It builds a TupleToUserset.

func Union

func Union(children ...Userset) Userset

Union returns the union of the given rewrites (the DSL `or`).

type UsersetUser

type UsersetUser struct {
	Type     string `json:"type"`
	ID       string `json:"id"`
	Relation string `json:"relation"`
}

UsersetUser references every user related to an object by a relation, e.g. the members of team:eng ({Type: "team", ID: "eng", Relation: "member"}).

type Usersets

type Usersets struct {
	Child []Userset `json:"child"`
}

Usersets holds the operands of a Union or Intersection.

type ValidationError

type ValidationError struct{ *ErrorResponse }

ValidationError is returned for HTTP 400 responses.

func (*ValidationError) Unwrap

func (e *ValidationError) Unwrap() error

Unwrap allows errors.As to reach the embedded *ErrorResponse.

type Wildcard

type Wildcard struct{}

Wildcard marks a type-bound wildcard reference (`user:*`). It serializes as an empty JSON object.

type WriteAssertionsRequest

type WriteAssertionsRequest struct {
	Assertions []Assertion `json:"assertions"`
}

WriteAssertionsRequest is the body for AssertionsService.Write.

type WriteAuthorizationModelRequest

type WriteAuthorizationModelRequest struct {
	SchemaVersion   string               `json:"schema_version"`
	TypeDefinitions []TypeDefinition     `json:"type_definitions"`
	Conditions      map[string]Condition `json:"conditions,omitempty"`
}

WriteAuthorizationModelRequest is the body sent to the Write method.

type WriteAuthorizationModelResponse

type WriteAuthorizationModelResponse struct {
	AuthorizationModelID string `json:"authorization_model_id"`
}

WriteAuthorizationModelResponse is returned by the Write method.

type WriteRequest

type WriteRequest struct {
	Writes               *WriteRequestTuples `json:"writes,omitempty"`
	Deletes              *WriteRequestTuples `json:"deletes,omitempty"`
	AuthorizationModelID string              `json:"authorization_model_id,omitempty"`
}

WriteRequest is the body for Tuples.Write.

type WriteRequestTuples

type WriteRequestTuples struct {
	TupleKeys   []TupleKey  `json:"tuple_keys"`
	OnDuplicate OnDuplicate `json:"on_duplicate,omitempty"`
	OnMissing   OnMissing   `json:"on_missing,omitempty"`
}

WriteRequestTuples carries a list of tuple keys for a write or delete operation. OnDuplicate is only meaningful on the Writes block; OnMissing is only meaningful on the Deletes block.

type WriteStatus

type WriteStatus string

WriteStatus reports the outcome of a single tuple in a bulk write/delete.

const (
	WriteStatusSuccess WriteStatus = "success"
	WriteStatusFailure WriteStatus = "failure"
)

WriteStatus values for a per-tuple bulk result.

type WriteTuplesResponse

type WriteTuplesResponse struct {
	Writes  []TupleResult
	Deletes []TupleResult
}

WriteTuplesResponse aggregates per-tuple outcomes. WriteTuples populates Writes; DeleteTuples populates Deletes.

func (*WriteTuplesResponse) Failed

func (r *WriteTuplesResponse) Failed() []TupleResult

Failed returns the per-tuple results that did not succeed, scanning both the Writes and Deletes slices so it serves WriteTuples and DeleteTuples alike. The result is nil when every tuple succeeded.

func (*WriteTuplesResponse) FirstError

func (r *WriteTuplesResponse) FirstError() error

FirstError returns the error from the first failed tuple, or nil if every tuple succeeded. Because WriteTuples and DeleteTuples report failures per-tuple (their returned error is non-nil only when no request could be issued at all), call this to detect a partial failure with one check.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL