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, ¬Found):
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)
}
}
Output:
Index ¶
- Constants
- type Assertion
- type AssertionsService
- type AuthenticationError
- type AuthorizationModel
- type AuthorizationModelsService
- func (s *AuthorizationModelsService) All(ctx context.Context, opts *ListModelsOptions, ropts ...RequestOption) iter.Seq2[AuthorizationModel, error]
- func (s *AuthorizationModelsService) Get(ctx context.Context, id string, opts ...RequestOption) (*AuthorizationModel, error)
- func (s *AuthorizationModelsService) List(ctx context.Context, opts *ListModelsOptions, ropts ...RequestOption) (*ListAuthorizationModelsResponse, error)
- func (s *AuthorizationModelsService) ReadLatest(ctx context.Context, opts ...RequestOption) (*AuthorizationModel, error)
- func (s *AuthorizationModelsService) Write(ctx context.Context, req *WriteAuthorizationModelRequest, ...) (*WriteAuthorizationModelResponse, error)
- type BatchCheckItem
- type BatchCheckRequest
- type BatchCheckResponse
- type BatchCheckSingleResult
- type CheckRequest
- type CheckRequestTupleKey
- type CheckResponse
- type Client
- func (c *Client) AuthorizationModelID() string
- func (c *Client) BareDo(req *http.Request) (*Response, error)
- func (c *Client) BaseURL() string
- func (c *Client) DefaultConsistency() ConsistencyPreference
- func (c *Client) Do(req *http.Request, v any) (*Response, error)
- func (c *Client) NewRequest(ctx context.Context, method, path string, body any, opts ...RequestOption) (*http.Request, error)
- func (c *Client) SetAuthorizationModelID(id string) error
- func (c *Client) SetDefaultConsistency(cons ConsistencyPreference)
- func (c *Client) SetStoreID(id string) error
- func (c *Client) StoreID() string
- func (c *Client) Transport() http.RoundTripper
- type ClientCredentialsConfig
- type Condition
- type ConditionMetadata
- type ConditionParamType
- type ConsistencyPreference
- type ContextualTupleKeys
- type CreateStoreRequest
- type Difference
- type DirectUserset
- type ErrorResponse
- type ExpandRequest
- type ExpandResponse
- type FGAObject
- type FGAObjectRelation
- type InternalError
- type ListAuthorizationModelsResponse
- type ListModelsOptions
- type ListObjectsRequest
- type ListObjectsResponse
- type ListRelationsRequest
- type ListStoresOptions
- type ListStoresResponse
- type ListUsersRequest
- type ListUsersResponse
- type Metadata
- type NotFoundError
- type ObjectRelation
- type OnDuplicate
- type OnMissing
- type Option
- func EnvOptions() ([]Option, error)
- func WithAPIToken(token string) Option
- func WithAuthorizationModelID(id string) Option
- func WithBaseTransport(rt http.RoundTripper) Option
- func WithBaseURL(raw string) Option
- func WithClientCredentials(cfg ClientCredentialsConfig) Option
- func WithDefaultConsistency(cons ConsistencyPreference) Option
- func WithHTTPClient(hc *http.Client) Option
- func WithHeaders(h http.Header) Option
- func WithPrivateKeyJWT(cfg PrivateKeyJWTConfig) Option
- func WithRequestObserver(obs RequestObserver) Option
- func WithRetry(cfg RetryConfig) Option
- func WithStoreID(id string) Option
- func WithTokenSource(src oauth2.TokenSource) Option
- func WithUserAgent(ua string) Option
- func WithoutRetry() Option
- type PrivateKeyJWTConfig
- type RateLimitError
- type ReadAssertionsResponse
- type ReadChangesOptions
- type ReadChangesResponse
- type ReadRequest
- type ReadRequestTupleKey
- type ReadResponse
- type RelationMetadata
- type RelationReference
- type RelationshipCondition
- type RelationshipsService
- func (s *RelationshipsService) Allowed(ctx context.Context, user, relation, object string, opts ...RequestOption) (bool, error)
- func (s *RelationshipsService) BatchCheck(ctx context.Context, req *BatchCheckRequest, opts ...RequestOption) (*BatchCheckResponse, error)
- func (s *RelationshipsService) BatchCheckAll(ctx context.Context, req *BatchCheckRequest, opts ...RequestOption) (*BatchCheckResponse, error)
- func (s *RelationshipsService) Check(ctx context.Context, req *CheckRequest, opts ...RequestOption) (*CheckResponse, error)
- func (s *RelationshipsService) Expand(ctx context.Context, req *ExpandRequest, opts ...RequestOption) (*ExpandResponse, error)
- func (s *RelationshipsService) ListObjects(ctx context.Context, req *ListObjectsRequest, opts ...RequestOption) (*ListObjectsResponse, error)
- func (s *RelationshipsService) ListRelations(ctx context.Context, req *ListRelationsRequest, opts ...RequestOption) ([]string, error)
- func (s *RelationshipsService) ListUsers(ctx context.Context, req *ListUsersRequest, opts ...RequestOption) (*ListUsersResponse, error)
- func (s *RelationshipsService) StreamedListObjects(ctx context.Context, req *ListObjectsRequest, opts ...RequestOption) iter.Seq2[StreamedListObjectsResponse, error]
- type RequestObserver
- type RequestOption
- func OnResponse(fn func(*Response)) RequestOption
- func WithAuthorizationModel(id string) RequestOption
- func WithConsistency(c ConsistencyPreference) RequestOption
- func WithMaxChecksPerBatch(n int) RequestOption
- func WithMaxParallel(n int) RequestOption
- func WithMaxPerChunk(n int) RequestOption
- func WithOnDuplicate(v OnDuplicate) RequestOption
- func WithOnMissing(v OnMissing) RequestOption
- func WithRequestHeader(key, value string) RequestOption
- func WithStore(storeID string) RequestOption
- func WithTransaction() RequestOption
- type Response
- type RetryConfig
- type SourceInfo
- type Store
- type StoresService
- func (s *StoresService) All(ctx context.Context, opts *ListStoresOptions, ropts ...RequestOption) iter.Seq2[Store, error]
- func (s *StoresService) Create(ctx context.Context, req *CreateStoreRequest, opts ...RequestOption) (*Store, error)
- func (s *StoresService) Delete(ctx context.Context, storeID string, opts ...RequestOption) error
- func (s *StoresService) Get(ctx context.Context, storeID string, opts ...RequestOption) (*Store, error)
- func (s *StoresService) List(ctx context.Context, opts *ListStoresOptions, ropts ...RequestOption) (*ListStoresResponse, error)
- type StreamedListObjectsResponse
- type Tuple
- type TupleChange
- type TupleKey
- type TupleResult
- type TupleToUserset
- type TuplesService
- func (s *TuplesService) ChangesAll(ctx context.Context, opts *ReadChangesOptions, ropts ...RequestOption) iter.Seq2[TupleChange, error]
- func (s *TuplesService) DeleteTuples(ctx context.Context, keys []TupleKey, opts ...RequestOption) (*WriteTuplesResponse, error)
- func (s *TuplesService) Read(ctx context.Context, req *ReadRequest, opts ...RequestOption) (*ReadResponse, error)
- func (s *TuplesService) ReadAll(ctx context.Context, req *ReadRequest, ropts ...RequestOption) iter.Seq2[Tuple, error]
- func (s *TuplesService) ReadChanges(ctx context.Context, opts *ReadChangesOptions, ropts ...RequestOption) (*ReadChangesResponse, error)
- func (s *TuplesService) Write(ctx context.Context, req *WriteRequest, opts ...RequestOption) error
- func (s *TuplesService) WriteTuples(ctx context.Context, keys []TupleKey, opts ...RequestOption) (*WriteTuplesResponse, error)
- type TypeDefinition
- type TypedWildcard
- type User
- type UserTypeFilter
- type Userset
- type UsersetUser
- type Usersets
- type ValidationError
- type Wildcard
- type WriteAssertionsRequest
- type WriteAuthorizationModelRequest
- type WriteAuthorizationModelResponse
- type WriteRequest
- type WriteRequestTuples
- type WriteStatus
- type WriteTuplesResponse
Examples ¶
Constants ¶
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 ¶
func (s *AssertionsService) Read(ctx context.Context, modelID string, opts ...RequestOption) (*ReadAssertionsResponse, error)
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")
}
Output:
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 ¶
func (s *AuthorizationModelsService) All(ctx context.Context, opts *ListModelsOptions, ropts ...RequestOption) iter.Seq2[AuthorizationModel, error]
All iterates every authorization model across pages. It copies opts so the caller's struct is never mutated.
func (*AuthorizationModelsService) Get ¶
func (s *AuthorizationModelsService) Get(ctx context.Context, id string, opts ...RequestOption) (*AuthorizationModel, error)
Get retrieves a single authorization model by ID.
func (*AuthorizationModelsService) List ¶
func (s *AuthorizationModelsService) List(ctx context.Context, opts *ListModelsOptions, ropts ...RequestOption) (*ListAuthorizationModelsResponse, error)
List returns a page of authorization models for the store.
func (*AuthorizationModelsService) ReadLatest ¶
func (s *AuthorizationModelsService) ReadLatest(ctx context.Context, opts ...RequestOption) (*AuthorizationModel, error)
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 ¶
func (s *AuthorizationModelsService) Write(ctx context.Context, req *WriteAuthorizationModelRequest, opts ...RequestOption) (*WriteAuthorizationModelResponse, error)
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)
}
Output:
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 ¶
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())
}
Output:
func NewClientFromEnv ¶
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())
}
Output:
func (*Client) AuthorizationModelID ¶
AuthorizationModelID returns the client's default authorization model ID (empty if unset).
func (*Client) BareDo ¶
BareDo executes a request through the transport chain, classifies errors, and returns the wrapped response without decoding the body.
func (*Client) DefaultConsistency ¶
func (c *Client) DefaultConsistency() ConsistencyPreference
DefaultConsistency returns the client's default read consistency.
func (*Client) Do ¶
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)
}
Output:
func (*Client) SetAuthorizationModelID ¶
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 ¶
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) 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 ¶
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 ¶
ExpandResponse is returned by Relationships.Expand. The tree is returned as an untyped map to accommodate the recursive, schema-version-dependent shape.
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 ¶
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").
type Option ¶
type Option func(*Client)
Option configures a Client during NewClient.
func EnvOptions ¶
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 ¶
WithAPIToken authenticates with a pre-shared key (Authorization: Bearer).
func WithAuthorizationModelID ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
WithUserAgent overrides the User-Agent header sent on every request.
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)
}
Output:
func (*RelationshipsService) BatchCheck ¶
func (s *RelationshipsService) BatchCheck(ctx context.Context, req *BatchCheckRequest, opts ...RequestOption) (*BatchCheckResponse, error)
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 ¶
func (s *RelationshipsService) BatchCheckAll(ctx context.Context, req *BatchCheckRequest, opts ...RequestOption) (*BatchCheckResponse, error)
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 ¶
func (s *RelationshipsService) Check(ctx context.Context, req *CheckRequest, opts ...RequestOption) (*CheckResponse, error)
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)
}
Output:
func (*RelationshipsService) Expand ¶
func (s *RelationshipsService) Expand(ctx context.Context, req *ExpandRequest, opts ...RequestOption) (*ExpandResponse, error)
Expand returns the userset tree that proves a relationship.
func (*RelationshipsService) ListObjects ¶
func (s *RelationshipsService) ListObjects(ctx context.Context, req *ListObjectsRequest, opts ...RequestOption) (*ListObjectsResponse, error)
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 ¶
func (s *RelationshipsService) ListUsers(ctx context.Context, req *ListUsersRequest, opts ...RequestOption) (*ListUsersResponse, error)
ListUsers returns all users who have a specific relation with a given object.
func (*RelationshipsService) StreamedListObjects ¶
func (s *RelationshipsService) StreamedListObjects(ctx context.Context, req *ListObjectsRequest, opts ...RequestOption) iter.Seq2[StreamedListObjectsResponse, error]
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)
}
}
Output:
type RequestObserver ¶
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 ¶
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)
}
}))
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 ¶
func (s *StoresService) All(ctx context.Context, opts *ListStoresOptions, ropts ...RequestOption) iter.Seq2[Store, error]
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)
}
}
Output:
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 ¶
func (s *StoresService) List(ctx context.Context, opts *ListStoresOptions, ropts ...RequestOption) (*ListStoresResponse, error)
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 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 ¶
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)
}
}
}
Output:
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 ¶
ComputedUserset returns a rewrite to another relation on the same object (the DSL bare-relation reference, e.g. `owner`).
func Intersection ¶
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).
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.
Source Files
¶
- assertions.go
- assertions_types.go
- authorizationmodels.go
- authorizationmodels_types.go
- doc.go
- errors.go
- model.go
- openfga.go
- pagination.go
- parallel.go
- relationships.go
- relationships_types.go
- request.go
- response.go
- services.go
- stores.go
- stores_types.go
- transport_auth.go
- transport_headers.go
- transport_observer.go
- transport_retry.go
- tuples.go
- tuples_types.go
- types.go
- validate.go