Typed handles
Use the Go structs and handles that ridu generate writes for each collection and global, so the compiler checks field names and types.
ridu generate writes a Go struct for each collection’s documents, creates and updates, plus a
handle that reads and writes them. Bind a handle to the Local API and the compiler checks every
field name and type. It runs the same operations as store.Values; only the types change.
Examples use the example schema. Its generated package is generated.
Bind a handle
posts := generated.PostsCollection.With(app.Local())Bind once and reuse the handle. In a hook, bind to ctx.Local so calls join the hook’s
transaction.
Create
post, err := posts.Create(ctx, generated.PostCreate{
Title: "Hello, Ridu",
Status: core.Set(generated.PostStatusPublished),
Tags: core.Set([]string{"launch", "go"}),
}, ridu.TypedMutationOptions{Actor: user})post is a generated.Post. Required fields, such as Title, are plain values; the others are
optional, as described in Optional and null values.
Read
page, err := posts.List(ctx, core.TypedListOptions{
Where: query.Equal("status", "published"),
Sort: []query.Sort{query.Desc("publishedAt")},
Actor: user,
})
if err != nil {
return err
}
for _, post := range page.Documents {
fmt.Println(*post.Title)
}List takes the same filters, sorting, pagination, selection and population as the dynamic API;
see Querying data. Find reads one document with core.TypedReadOptions.
Output fields are pointers because access rules or a select can leave any of them out. A
relationship is a generated.Reference[T] with the target’s ID, and its Document once you
populate it:
post, err := posts.Find(ctx, postID, core.TypedReadOptions{
Populate: []query.Population{{Path: query.Field("author")}},
Actor: user,
})
if err != nil {
return err
}
if post.Author != nil && post.Author.Document != nil {
fmt.Println(*post.Author.Document.Name)
}Update and delete
title := "A better idea"
post, err := posts.Update(ctx, postID, generated.PostUpdate{
Title: &title,
Tags: core.Null[[]string](),
}, ridu.TypedMutationOptions{Actor: user})This retitles the post and clears its tags; fields you leave nil keep their values. Delete
takes the ID and the same options.
Optional and null values
An optional field in a create or update struct is a *core.Input[T]:
| Write | To |
|---|---|
nil |
leave the field out, keeping its stored value or default |
core.Set(v) |
set it to v |
core.Null[T]() |
clear it |
A required field is a plain value in a create, and a *T in an update because you may leave it
out; it can’t be cleared. Lists, maps and some plugin types that can never be null use
core.NonNullInput[T]: set them with core.NonNull(v) when required, or core.SetNonNull(v) when
optional.
Publish a versioned document
In a versioned collection, a published document changes by being published again. Send the changes
to PublishChanges with the revision you read:
pages := generated.PagesCollection.With(app.Local())
title := "About us"
updated, err := pages.PublishChanges(ctx, page.ID, generated.PageUpdate{
Title: &title,
}, ridu.TypedMutationOptions{
ExpectedRevision: page.Revision,
Actor: user,
})Update refuses a published document here with publish_required. Publish publishes a saved
draft, and Unpublish takes a document offline and keeps its content. See
Update a versioned document for revision checks.
Globals
settings := generated.SiteSettingsGlobal.With(app.Local())
current, err := settings.Update(ctx, generated.SiteSettingsUpdate{
SiteName: core.Set("Acme"),
}, ridu.TypedMutationOptions{Actor: user})A global handle has Find and Update, and for a versioned global PublishChanges, Publish,
Unpublish, Restore, RestoreAsDraft, SaveDraft and DiscardDraft.
What a handle covers
A collection handle has Create, Find, List, Update, Delete and Import; for versioned
collections, PublishChanges, Publish and Unpublish; and for collections with drafts,
CreateDraft, SaveDraft and DiscardDraft. For bulk actions, trash, versions, locale copies
and inverse joins, use the dynamic Local API from the operation table
with the same ctx and caller.
A localized project also gets AllLocales read bindings, whose fields hold every locale’s value.
Typed writes always target one locale.