Collections

A collection block defines a data model — a database table (in SQL databases) or a document collection (in document databases). Each collection automatically gets CRUD API endpoints.

Syntax

collection <name> {
  auth: true
  fields: {
    // field definitions
  }
  realtime: { ... }
  cache: { ... }
  hooks: { ... }
  admin: { ... }
}

The collection name must be a valid identifier ([a-zA-Z_][a-zA-Z0-9_]*) and must be unique across all .radiant files.

Allowed Properties

PropertyTypeDescription
authBooleanMarks this collection as the authentication collection. Only one collection should have auth: true.
fieldsObjectThe field definitions for this collection. See Field Types.
realtimeObjectRealtime subscription settings (WebSocket, SSE, durable streams).
cacheObjectCaching settings (TTL, strategy).
hooksObjectLifecycle hook references.
adminObjectAdmin UI settings for this collection.

auth

When auth: true, the collection is treated as the user collection. The runtime enables login, registration, password reset, and token refresh endpoints for it. The runtime also hashes the password field automatically.

collection users {
  auth: true
  fields: {
    name: text
    email: email @unique
    password: password
    role: text @default("user")
  }
}

fields

The fields block contains field definitions as key-value pairs. Each key is the field name, and the value is the field type (an identifier or function call):

collection todos {
  fields: {
    title: text
    completed: boolean @default(false)
    priority: select("low", "medium", "high")
    author: relationship("users")
    tags: text[]
    metadata: json @optional
  }
}

Every collection automatically gets an id field (UUID string), plus createdAt and updatedAt timestamp fields managed by the runtime.

See the Field Types page for all available types and the Decorators page for @unique, @optional, @default, etc.

realtime

Realtime subscriptions push change events to connected clients in real time.

collection todos {
  realtime: {
    ws: ["create", "update", "delete"]
    sse: true
    durableStream: false
  }
  fields: {
    // ...
  }
}
PropertyTypeDescription
wsString[]Events to broadcast over WebSocket: "create", "update", "delete".
sseBooleanEnable Server-Sent Events for this collection.
durableStreamBooleanEnable a durable stream (persisted event log) for replay.

cache

Per-collection caching with configurable TTL and strategy:

collection products {
  cache: {
    ttl: 3600
    strategy: "stale-while-revalidate"
  }
  fields: {
    // ...
  }
}
PropertyTypeDescription
ttlNumberTime-to-live in seconds.
strategyStringCache strategy: "stale-while-revalidate", "cache-first", "network-first".

hooks

Lifecycle hooks run before or after CRUD operations. Hook handlers are written in TypeScript in src/hooks.ts (not in the DSL). The hooks property in the DSL is a reference/flag that the runtime uses to register them:

collection todos {
  hooks: {
    beforeCreate: true
    afterCreate: true
    beforeUpdate: true
  }
  fields: {
    // ...
  }
}

The actual hook logic is defined in TypeScript:

// src/hooks.ts
import { app } from "./app";

app.hooks("todos", {
  beforeCreate: async (ctx) => {
    if (!ctx.data.author) ctx.data.author = ctx.user?.id || "anonymous";
    return ctx.data;
  },
  afterCreate: async (ctx) => {
    console.log("Todo created:", ctx.data.id);
  },
});

admin

Admin UI settings for this collection:

collection products {
  admin: {
    list: ["name", "price", "status"]
    searchable: true
    pageSize: 25
  }
  fields: {
    // ...
  }
}

Auto-Generated API Routes

Every collection gets these REST endpoints (mounted under the API prefix):

MethodPathDescription
GET/<slug>List records with filtering, pagination, and sorting.
POST/<slug>Create a new record.
GET/<slug>/:idGet a single record by ID.
PUT/<slug>/:idUpdate a record by ID.
DELETE/<slug>/:idDelete a record by ID.

For a collection named todos with API prefix /api:

GET    /api/todos
POST   /api/todos
GET    /api/todos/:id
PUT    /api/todos/:id
DELETE /api/todos/:id

Cross-File References

Collections can reference each other across files. The compiler merges all .radiant files and validates relationship targets:

// radiant/users.radiant
collection users {
  auth: true
  fields: {
    name: text
    email: email @unique
  }
}
// radiant/posts.radiant
collection posts {
  fields: {
    title: text
    author: relationship("users")  // references the users collection
  }
}

If relationship("users") targets a collection that doesn't exist, the compiler produces a semantic error.