Local Go API
Read and write content from Go without HTTP, through the same access rules, validation, and hooks as every other API.
app.Local() gives Go code the same operations as REST and the TypeScript SDK, without an HTTP
round trip. It is not a back door to the database: access rules, validation, hooks, versions and
transactions run exactly as they do for every other API.
Get the Local API
Call app.Local() in your own handlers, services and tasks. Hooks, access rules and custom endpoints
receive it as ctx.Local:
func publishedPosts(
ctx context.Context,
app *ridu.App,
user *store.Document,
) (store.Page, error) {
return app.Local().List(ctx, "posts", ridu.ListOptions{
Where: query.Equal("status", "published"),
Actor: user,
})
}func countAuthorPosts(ctx ridu.HookContext) error {
authorID, _ := ctx.Document.Values["author"].StringValue()
page, err := ctx.Local.List(ctx.Context, "posts", ridu.ListOptions{
Where: query.Equal("author", authorID),
Limit: 1,
Actor: ctx.Actor,
ActorCollection: ctx.ActorCollection,
})
if err != nil {
return err
}
_ = *page.Total // the author's post count
return nil
}A call made inside a hook joins the transaction of the operation that ran the hook; see Transactions and errors.
What it can do
| To… | Use | Guide |
|---|---|---|
| read documents and globals | Find, List, Global, Distinct |
Querying data |
| create, change and remove them | Create, Update, Delete, Duplicate, UpdateGlobal |
Writing data |
| publish, and work with history | PublishChanges, Publish, Unpublish, DiscardDraft, Versions, Restore |
Drafts and versions |
| change many documents at once | BulkUpdate, BulkPublish, BulkUnpublish, BulkDelete |
Bulk actions and trash |
| restore or empty the trash | RestoreDeleted, DeletePermanent, EmptyTrash |
Bulk actions and trash |
| copy values between locales | CopyLocale, CopyGlobalLocale |
Localization |
| read or change an inverse join | ListJoin, MutateJoin |
Relationships and joins |
| ask what the caller may do | Capabilities |
Access control |
| import documents from elsewhere | Import, which keeps their IDs, status and timestamps |
Move from Payload |
Versioned globals have matching Global… methods, such as PublishGlobalChanges. Files go through
the upload methods on App; see Uploads.
Work with values
The Local API takes and returns store.Values, a map from field names to values. Build values with
store.String, store.Number, store.Boolean, store.List, store.Object and store.Null, and
read them back with methods such as StringValue:
post, err := local.Create(ctx, "posts", store.Values{
"title": store.String("Hello, Ridu"),
"views": store.Number(0),
}, ridu.MutationOptions{Actor: user})
if err != nil {
return err
}
title, _ := post.Values["title"].StringValue()Returned documents are copies: changing them changes nothing stored. A relationship is its target’s ID unless you populate it. Documents and values covers nested values and the difference between leaving a field out and clearing it. For compile-time checked structs instead, use typed handles.
Pass the caller
Every call takes the document of the user it acts for as Actor, and access rules and hooks see it.
nil means an anonymous visitor, never an administrator.
When your application has more than one auth collection, an ID alone doesn’t say which user it is. Pass the collection as well:
session, err := app.Session(ctx, rawSessionToken)
if err != nil {
return err
}
post, err := app.Local().Find(ctx, "posts", postID, ridu.FindOptions{
Actor: &session.User,
ActorCollection: session.Collection,
})Copy both from the identity your authentication established, such as a ridu.AuthIdentity.
Services built around one user, such as scheduling, previews, preferences and document locks, take
an AuthIdentity directly.
Server-owned work
Some writes belong to the server rather than the user who triggered them: a hook that updates a
counter, or enrolls a league’s owner. Set System: true for those calls instead of loosening access
rules for everyone:
func enrolOwner(ctx ridu.HookContext) error {
_, err := ctx.Local.Create(ctx.Context, "memberships", store.Values{
"league": store.String(ctx.Document.ID),
"learner": store.String(ctx.Actor.ID),
}, ridu.MutationOptions{
System: true,
Actor: ctx.Actor,
ActorCollection: ctx.ActorCollection,
})
return err
}A system call skips access rules, including field access, and reads drafts unless you set Draft
to false. Validation, hooks, versions and the transaction still run, and Actor still tells hooks
whom the work is for. Nested calls don’t inherit System; pass it again only for work that is also
trusted. Only Go can set it: no transport accepts it from a client.
Options
Each method takes its required values positionally and a final options struct:
| Options | Used by | Holds |
|---|---|---|
FindOptions |
Find, Global |
Select, Populate, Draft, TrashOnly, locale, and the caller |
ListOptions |
List |
everything in FindOptions, plus Where, Sort, Page, Limit and SkipTotal |
MutationOptions |
writes and publishing | the caller or System, ExpectedRevision, Draft, the write locale, and returned population |
BulkOptions |
bulk actions, EmptyTrash |
the caller or System, and locale |
CapabilityOptions |
Capabilities |
candidate Data, the caller, trash mode and locale |
ImportOptions |
Import |
the caller or System, and source metadata |
Draft is a *bool, so leaving it out, true and false mean different things. On a read, true
asks for the working draft and false for the published version. On a create, true allows an
incomplete draft and false validates and publishes at once. See
Drafts and versions for what leaving it out does.
Select controls stored fields; OutputFields separately controls computed fields and inverse
joins. AllLocales is for reads only; write one locale at a time, or use CopyLocale.
Next
- Typed handles: generated structs instead of
store.Values. - Transactions and errors: nested calls, rollbacks, and handling
*ridu.OperationError. - The Go API reference lists every method and option.