Browse all documentation

Collections and globals

Use collections for posts and products, globals for site settings, and add the features each needs.

Collections hold many documents, such as posts, products, people, or media. Globals hold one document, such as site settings or the main navigation. Both support fields, permissions, validation, hooks, and translations.

Define them with ridu.Collection and ridu.Global. Ridu generates the matching API, Go and TypeScript types, and admin screens.

Define a collection

A collection slug, such as articles, identifies it in the API, relationships, and admin URLs. Use Fields to list the fields each article should have.

content/articles.go
package content

import (
  "github.com/riducms/ridu"
  "github.com/riducms/ridu/field"
)

var Articles = ridu.Collection{
  Slug: "articles",
  Labels: ridu.CollectionLabels{
    Singular: "Article",
    Plural:   "Articles",
  },
  Admin: ridu.CollectionAdmin{
    UseAsTitle:     "title",
    DefaultColumns: []string{"title", "category", "summary"},
    Group:          "Editorial",
    Description:    "Long-form stories published on the site.",
  },
  Fields: field.Fields{
    field.Text("title").Required(),
    field.Textarea("summary"),
    field.Relationship("category", "categories"),
    field.Group("seo", field.Fields{
      field.Text("slug").Required(),
    }),
  },
  Indexes: []ridu.CollectionIndex{
    {Fields: []string{"category", "seo.slug"}, Unique: true},
  },
}

Register the value in ridu.Config.Collections. Config resolution rejects unknown relationship targets, invalid admin fields, duplicate slugs, and unsupported index paths.

Collection settings

Property Purpose
Slug URL-safe public resource name. It is used by APIs and relationships, not just the admin.
Labels Optional singular/plural author-facing names. Ridu derives readable defaults from the slug.
Admin Navigation, list, hierarchy, folder, and live-preview presentation. It never grants access.
Fields Stored, layout, computed, relationship, upload, and plugin field definitions.
Indexes Ordered compound store indexes; use field-level Unique() for one-field uniqueness.
Auth, AuthConfig Make documents identities and configure passwords, sessions, recovery, API keys, and strategies.
Upload, UploadConfig Make documents file records backed by Config.Storage.
Versions, VersionConfig Record snapshots and optionally enable draft authoring.
Trash Replace ordinary delete with recoverable trash, restore, and permanent-delete operations.
LockDocuments, DocumentLockConfig Persist editor locks and controlled takeover for coordinated authoring.
Access Authorize resource operations.
Hooks Run deterministic lifecycle callbacks, including transaction-reusing nested local calls.

See Fields for examples. A field name becomes a stored property and appears in generated types; its label and description only change what authors see.

Customize the collection in the admin

CollectionAdmin configures browsing and editing without changing API authorization:

Property Current behaviour
Hidden Leaves the collection out of admin navigation, the dashboard, and search; its admin pages show not-found.
UseAsTitle Names a direct field used as the document label in lists, relationships, and upload flows.
DefaultColumns Names unique direct fields shown by a fresh list workspace. Authors can persist their own workspace preferences.
Group Groups the collection in admin navigation.
Description Adds author-facing context for the resource.
FolderField Names a singular, non-polymorphic relationship used to filter a collection into folders.
ParentField Names a singular relationship back to the same collection and enables the hierarchy view.
LivePreview Configures the editor preview URL and optional named viewport sizes.

UseAsTitle, columns, folder, and parent fields must name existing direct fields. Folder relationships may target another collection; a parent relationship must target the collection itself. Live-preview URL templates accept {id}, {collection}, and {field:path.to.value} placeholders. Read Admin and Editorial workflows for the authoring experience.

Hide a collection from the admin

Set Hidden for data the application maintains itself and editors never browse, such as event logs or computed state:

go
{
  Slug:   "learner-events",
  Admin:  ridu.CollectionAdmin{Hidden: true},
  Fields: field.Fields{ /* ... */ },
}

A hidden collection is still a complete collection. The REST, GraphQL, and Local APIs serve it, its access rules and hooks run, and deletes cascade through it as usual. A relationship to it from a visible collection still shows the related document’s title, without a link to open it. To keep data from a user, deny access instead; Hidden only changes what the admin shows. GlobalAdmin.Hidden does the same for a global.

Admin settings and labels never change stored data, so changing Hidden, Group, a label, or any other CollectionAdmin or field Admin setting needs no migration.

Index several fields together

Use CollectionIndex to index a combination of fields. For example, an external ID can be unique within each tenant while still being reused by a different tenant:

go
Indexes: []ridu.CollectionIndex{
  {Fields: []string{"tenant", "seo.slug"}},
  {Fields: []string{"tenant", "externalID"}, Unique: true},
},

An index contains 2–32 unique, ordered paths. Paths can pass through non-repeated groups and end at a supported scalar or a singular, non-polymorphic relationship/upload reference. Arrays, blocks, rich text, objects, has-many references, and polymorphic references are not index terminals.

Official stores use PostgreSQL-style NULLS DISTINCT semantics: a unique tuple can appear more than once when any component is null or absent. Localized values are unique per exact locale, not across a fallback result. Trashed documents leave the active unique set; restoring one can fail with conflict if another active document has claimed its tuple.

Add accounts, uploads, drafts, or trash

A collection supports create, duplicate, find, list, update, delete, filtering, pagination, selection, population, access evaluation, hooks, and generated contracts. Flags add features:

Configuration What it adds
Auth: true User accounts, passwords, sessions, account recovery, and optional API keys
Upload: true File records, image processing, and file delivery; configure Config.Storage and StorageNamespace too
Versions: true Saved history, restore, optional drafts, and scheduled publishing
Trash: true Recoverable deletion, restore, and permanent deletion
LockDocuments: true Editing locks and authorized takeover

The official database adapters support these features; MongoDB requires its documented production setup. If you write your own adapter, it must implement support for each feature you enable. Ridu checks that when the application starts. Continue with Authentication, Uploads, Drafts and versions, and Editorial workflows for each feature.

Set permissions and run hooks

CollectionAccess has independent Admin, Create, Read, ReadVersions, ReadDrafts, Reference, Update, Publish, Unpublish, Delete, and Unlock rules. Nil ReadVersions and Reference rules fall back to Read; nil Publish, Unpublish, and Unlock rules fall back to Update. A nil ReadDrafts rule lets only editors read drafts. Read and mutation rules can return ridu.Where(...), which the store must combine atomically with the caller’s filter.

Attach field access and hooks directly with .Access(field.Access{...}) and .Hooks(field.Hooks[T]{...}). Reusing the field also reuses its rules and hooks. Root field.Virtual values own their resolver and can be limited with output selection. Learn the order and transaction boundaries in Access control and Hooks.

Globals

Use a global for content that has one instance, such as your site settings. Before it is first saved, reading it returns its fields with their defaults. Its first update saves the document using the global slug as its ID.

content/globals.go
package content

import (
  "github.com/riducms/ridu"
  "github.com/riducms/ridu/field"
)

var SiteSettings = ridu.Global{
  Slug:  "site-settings",
  Label: "Site settings",
  Admin: ridu.GlobalAdmin{
    Group:       "Settings",
    Description: "Site-wide identity and support details.",
  },
  Fields: field.Fields{
    field.Text("siteName").Required(),
    field.Email("supportEmail"),
  },
  Access: ridu.GlobalAccess{
    Read:   publicRead,
    Update: administratorsOnly,
  },
}

Register it in ridu.Config.Globals. A global has no collection-style create, duplicate, list, delete/trash, auth, upload, document-lock, folder, hierarchy, or compound-index configuration. Its supported surface is smaller:

Concern Collection Global
Cardinality Many documents with generated IDs One document whose ID is its slug
Base operations Create, duplicate, find/list, update, delete, bulk Read and update
Access CollectionAccess per CRUD/version/draft/admin/lock operation GlobalAccess.Read, ReadVersions, ReadDrafts, Update, Publish, and Unpublish
Admin Title, columns, folders, hierarchy, group, description, preview, hidden Group, description, preview, hidden
Optional capabilities Auth, upload, versions/drafts, trash, locks Versions/drafts only
Versions Per-document history, publish/unpublish, restore, scheduling Singleton history, publish/unpublish, restore; scheduled global publishing is not implemented

For a missing singleton, a filtered GlobalAccess.Update decision cannot match a row; the first update therefore requires an unconditional Allow. Once persisted, filtered read/update/version decisions are applied atomically to the row or each snapshot.

Renames and migrations

Rename the authored slug or field name, regenerate, then run ridu migrate create. Migration creation presents compatible remove/add pairs for confirmation and records accepted continuity in the immutable artifact. SQLite does this for fields; renaming a SQLite collection needs a named compiled transform. Do not maintain hand-authored public schema IDs or edit generated manifests to force a rename.

This changes the public field from title to headline; the migration must preserve the stored value:

content/articles.go
Fields: field.Fields{
  field.Text("title").Required(),
  field.Text("headline").Required(),
},

Review the resulting storage and API change—especially relationships, indexes, auth identities, localized fields, and removed capabilities—before applying it. See PostgreSQL migrations, SQLite, and Generated contracts.