REST API

Lucent automatically generates RESTful APIs for your collections.

Default Endpoints

For a collection named posts:

MethodEndpointDescription
GET/api/postsList all records
GET/api/posts/:idGet single record
POST/api/postsCreate record
PATCH/api/posts/:idUpdate record
DELETE/api/posts/:idDelete record

Query Parameters

Pagination

GET /api/posts?page=2&perPage=20

Cursor-Based Pagination

For efficient pagination on large datasets:

GET /api/posts?cursor=eyJpZCI6IjEyMyJ9&perPage=20

Response includes nextCursor for subsequent requests:

{
  "data": [...],
  "meta": {
    "hasNextPage": true,
    "nextCursor": "eyJpZCI6IjE2MyJ9"
  }
}

Filtering

Filters are passed as a URL-encoded JSON object in the where query parameter:

# Simple equality
GET /api/posts?where={"status":{"eq":"published"}}

# Multiple conditions (implicit AND)
GET /api/posts?where={"status":{"eq":"published"},"author":{"eq":"user-123"}}

Scalar operators

OperatorExampleDescription
eq{"age":{"eq":30}}Equal
neq{"status":{"neq":"draft"}}Not equal
gt{"score":{"gt":80}}Greater than
gte{"score":{"gte":80}}Greater than or equal
lt{"age":{"lt":65}}Less than
lte{"age":{"lte":65}}Less than or equal
in{"status":{"in":["draft","published"]}}Value is in the list
nin{"status":{"nin":["archived"]}}Value is NOT in the list
exists{"deletedAt":{"exists":false}}Field is NULL / NOT NULL
like{"title":{"like":"hello"}}Case-insensitive substring match

Array / multiselect operators

Use these on array or multiselect fields:

OperatorExampleDescription
contains{"tags":{"contains":"sports"}}Array field includes this value
notContains{"tags":{"notContains":"sports"}}Array field does NOT include this value
any{"tags":{"any":["sports","news"]}}Array field contains at least one of the values
all{"tags":{"all":["sports","news"]}}Array field contains ALL of the values

Date / number range shorthand

between is sugar for gte start AND lte end:

# Date range
GET /api/posts?where={"createdAt":{"between":["2024-01-01","2024-12-31"]}}

# Numeric range
GET /api/posts?where={"score":{"between":[50,100]}}

Compound filters (OR / AND)

GET /api/posts?where={"or":[{"status":{"eq":"published"}},{"featured":{"eq":true}}]}

GET /api/posts?where={"and":[{"score":{"gte":80}},{"tags":{"contains":"featured"}}]}

Sorting

GET /api/posts?sort=createdAt:desc
GET /api/posts?sort=title:asc
GET /api/posts?sort=status:asc,createdAt:desc

Field Selection

GET /api/posts?fields=id,title,slug

Relations

GET /api/posts?include=author,tags
GET /api/posts?search=keyword

Response Format

List Response

{
  "data": [...],
  "meta": {
    "total": 100,
    "page": 1,
    "perPage": 20,
    "totalPages": 5
  }
}

Cursor Pagination Response

{
  "data": [...],
  "meta": {
    "hasNextPage": true,
    "hasPrevPage": true,
    "nextCursor": "eyJpZCI6IjE2MyJ9",
    "prevCursor": "eyJpZCI6IjEyMyJ9"
  }
}

Custom Endpoints

Create custom API endpoints by defining a LucentEndpoint — a function that receives the Elysia app and registers routes on it:

// src/endpoints/posts.ts
import type { LucentEndpoint } from "../lucent-types"; // generated — use this for full type safety

export const postsEndpoint: LucentEndpoint = (app) => {
  app.get("/posts/published", async ({ lucent, user }) => {
    const result = await lucent.find("posts", {
      where: { status: { eq: "published" } },
      sort: "-createdAt",
      limit: 10,
    });
    return result;
  });
};

The handler context provides:

  • lucent — the Lucent Local API for querying collections directly
  • user — the authenticated AuthUser | null resolved from the request token/session

Typed body and response schemas

Export a TypeBox t.Object(...) schema from your endpoint file and Lucent will automatically generate a TypeScript interface for it in lucent-types.ts — using the exact export name:

// src/endpoints/auth.ts
import { t } from "elysia";
import type { LucentEndpoint } from "../lucent-types";

export const AuthBodySchema = t.Object({
  name: t.String(),
  file: t.File(),
});

export const myAuth: LucentEndpoint = (app) => {
  app.post(
    "/auth/upload",
    ({ body }) => {
      /* body is typed */
    },
    {
      body: AuthBodySchema,
    },
  );
};

After lucent build, lucent-types.ts will contain:

export interface AuthBodySchema {
  name: string;
  file: File;
}

Register endpoints in your config:

export default defineLucentConfig({
  endpoints: [postsEndpoint, myAuth],
});

Batch Operations

# Create multiple records
POST /api/posts
Content-Type: application/json

{
  "data": [
    { "title": "Post 1", "content": "..." },
    { "title": "Post 2", "content": "..." }
  ]
}

Soft Delete

Collections with softDelete: true will automatically filter deleted records:

# Records with deletedAt set are excluded from normal queries
GET /api/posts

# Include soft-deleted records
GET /api/posts?includeDeleted=true