Browse all documentation

Collections and globals

Define repeatable documents and singleton configuration, then add only the capabilities each resource needs.

Collections hold many documents—posts, products, people, media. Globals hold exactly one logical document—site settings, navigation, or a home-page composition. Both use the same fields, access, validation, hooks, localization, and field redaction.

ridu.Collection and ridu.Global are typed Go config values. Their serializable shape becomes generated Go and TypeScript contracts, OpenAPI, and the admin model.

Define a collection

A collection slug is its durable public address in REST, generated clients, relationships, and the admin. Presentation belongs in Labels and Admin; runtime behaviour belongs in fields, access, hooks, and explicit capabilities.

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.Definition{
    field.Text("title", field.Required()),
    field.Textarea("summary"),
    field.Relationship("category", field.To("categories")),
    field.Group("seo", field.Fields(
      field.Text("slug", field.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 configuration map

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, FieldAccess Authorize resource operations and redact or reject individual field paths.
Hooks, FieldHooks Run deterministic lifecycle callbacks, including transaction-reusing nested local calls.
Computed Resolve virtual response fields after storage without persisting them.

See Fields for every builder and option. A field’s name is part of stored and generated contracts; its label and description affect presentation only.

Admin metadata is presentation

CollectionAdmin configures browsing and editing without changing API authorization:

Property Current behaviour
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.

Compound indexes

Use CollectionIndex when the tuple, rather than one field, is indexed or unique:

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.

Opt-in capabilities

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

Configuration Adds Required runtime contract
Auth: true Password credentials, sessions, account lockout, recovery/verification, optional API keys, custom request strategies Store implements store.AuthStore; the production server also expects auth maintenance support
Upload: true Server-owned file metadata, validation, image variants, delivery, regeneration, and cleanup Config.Storage, StorageNamespace, and upload-aware store capabilities
Versions: true _revision, _status, retained snapshots, restore, and scheduled collection publishing Store implements store.VersionTransaction; scheduling needs durable task or publish-job support
Trash: true Soft delete, trash-only reads, restore, permanent delete, empty trash, and cleanup of owned state Store implements the trash and reference-cleanup contracts
LockDocuments: true Inspect/acquire/release locks and authorized takeover Store implements store.DocumentLockStore

All three official database adapters supply these store capabilities; MongoDB does so inside its bounded production profile. Application construction fails if an adapter cannot support an enabled feature. Continue with Authentication, Uploads, Drafts and versions, and Editorial workflows for each feature.

Access, hooks, and computed output

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

FieldAccess and FieldHooks maps use authored paths such as seo.slug. Repeated field occurrences also receive a concrete runtime path. Computed values are response-only and can be limited with local API output selection. Learn the order and transaction boundaries in Access control and Hooks.

Globals

Globals use singleton routes and generated singleton handles. Reading a global before its first write returns a schema-shaped value with defaults; the first update persists the row under the global slug.

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.Definition{
    field.Text("siteName", field.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/admin/lock operation GlobalAccess.Read, ReadVersions, Update, Publish, and Unpublish
Admin Title, columns, folders, hierarchy, group, description, preview Group, description, preview
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. PostgreSQL migration creation presents compatible remove/add pairs for confirmation and records accepted continuity in the immutable artifact. SQLite requires a named compiled transform to rewrite stored canonical JSON. 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.Definition{
  field.Text("title", field.Required()),
  field.Text("headline", field.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.