REST API
Lucent automatically generates RESTful APIs for your collections.
Default Endpoints
For a collection named posts:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/posts | List all records |
| GET | /api/posts/:id | Get single record |
| POST | /api/posts | Create record |
| PATCH | /api/posts/:id | Update record |
| DELETE | /api/posts/:id | Delete 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
| Operator | Example | Description |
|---|---|---|
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:
| Operator | Example | Description |
|---|---|---|
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
Search
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 directlyuser— the authenticatedAuthUser | nullresolved 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