APIs
TrailBase automatically generates type-safe REST APIs for your database tables and views, with built-in validation, access control, and TypeScript support.
API Overview
TrailBase provides three main API categories:
Record APIs Auto-generated CRUD APIs for tables/views
Auth APIs User authentication and session management
Admin APIs System administration and configuration
Base URL Structure
http://localhost:4000/api/{category}/{version}/{endpoint}
category : records, auth, admin
version : v1 (API versioning for future changes)
endpoint : Specific operation path
Record APIs
Record API Module Record APIs provide CRUD operations with automatic type validation and access control.
Endpoint Structure
All record API endpoints follow this pattern:
/api/records/v1/{api_name}/{operation}
From records/mod.rs:50-97, the router defines:
pub ( crate ) fn router ( enable_transactions : bool ) -> Router < AppState > {
Router :: new ()
// Get single record
. route ( "/api/records/v1/{name}/{record}" , get ( read_record_handler ))
// Create record
. route ( "/api/records/v1/{name}" , post ( create_record_handler ))
// Update record
. route ( "/api/records/v1/{name}/{record}" , patch ( update_record_handler ))
// Delete record
. route ( "/api/records/v1/{name}/{record}" , delete ( delete_record_handler ))
// List records
. route ( "/api/records/v1/{name}" , get ( list_records_handler ))
// Get JSON schema
. route ( "/api/records/v1/{name}/schema" , get ( json_schema_handler ))
// Subscribe to record changes (SSE)
. route ( "/api/records/v1/{name}/subscribe/{record}" , get ( add_subscription_sse_handler ))
// File access
. route ( "/api/records/v1/{name}/{record}/file/{column_name}" , get ( get_uploaded_file_from_record_handler ))
}
Create Record
Endpoint : POST /api/records/v1/{api_name}
curl -X POST http://localhost:4000/api/records/v1/posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_token>" \
-d '{
"title": "My First Post",
"content": "Hello, world!",
"published": false
}'
Response :
{
"id" : 123 ,
"title" : "My First Post" ,
"content" : "Hello, world!" ,
"user_id" : { "id" : "base64_user_id" },
"published" : false ,
"created_at" : 1704067200000
}
Implemented in records/create_record.rs. Fields are validated against the table’s JSON schema.
Read Record
Endpoint : GET /api/records/v1/{api_name}/{record_id}
curl http://localhost:4000/api/records/v1/posts/123
Query Parameters :
expand: Comma-separated foreign keys to expand
# With foreign key expansion
curl "http://localhost:4000/api/records/v1/posts/123?expand=user_id,category_id"
Response (with expansion):
{
"id" : 123 ,
"title" : "My First Post" ,
"content" : "Hello, world!" ,
"user_id" : {
"id" : "base64_user_id" ,
"data" : {
"id" : "base64_user_id" ,
"email" : "[email protected] " ,
"verified" : true
}
},
"published" : false ,
"created_at" : 1704067200000
}
Read Implementation Handles record fetching, foreign key expansion, and access control.
Update Record
Endpoint : PATCH /api/records/v1/{api_name}/{record_id}
curl -X PATCH http://localhost:4000/api/records/v1/posts/123 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_token>" \
-d '{
"published": true
}'
Features :
Partial updates (only send changed fields)
Validates against schema
Checks update access rules
Returns updated record
Implemented in records/update_record.rs with conflict resolution support.
Delete Record
Endpoint : DELETE /api/records/v1/{api_name}/{record_id}
curl -X DELETE http://localhost:4000/api/records/v1/posts/123 \
-H "Authorization: Bearer <auth_token>"
Response : 204 No Content on success
Deletions are permanent and cannot be undone. Implement soft deletes if needed.
List Records
List Implementation Provides filtering, sorting, pagination, and aggregation.
Endpoint : GET /api/records/v1/{api_name}
Query Parameters :
Parameter Type Description filterstring SQL WHERE clause expression orderstring Column to sort by limitinteger Max records to return offsetinteger Number of records to skip countboolean Include total count expandstring Foreign keys to expand
Example :
curl "http://localhost:4000/api/records/v1/posts?filter=published%3D1&order=created_at%20DESC&limit=10&count=true"
Response :
{
"records" : [
{
"id" : 123 ,
"title" : "My First Post" ,
"content" : "Hello, world!" ,
"published" : true ,
"created_at" : 1704067200000
}
],
"total_count" : 42
}
Filtering
The filter parameter accepts SQL WHERE clause expressions:
# Single condition
? filter = published = 1
# Multiple conditions
? filter = published = 1 AND created_at > 1704067200000
# String matching
? filter = title LIKE '%tutorial%'
# NULL checks
? filter = deleted_at IS NULL
# IN clause
? filter = category_id IN (1, 2, 3 )
Filter expressions are validated and sanitized to prevent SQL injection.
JSON Schema Endpoint
Endpoint : GET /api/records/v1/{api_name}/schema
curl http://localhost:4000/api/records/v1/posts/schema
Response :
{
"$schema" : "https://json-schema.org/draft/2020-12/schema" ,
"title" : "post" ,
"type" : "object" ,
"properties" : {
"id" : { "type" : "integer" },
"title" : { "type" : "string" },
"content" : { "type" : [ "string" , "null" ] },
"published" : { "type" : "boolean" },
"created_at" : { "type" : "integer" }
},
"required" : [ "title" , "published" , "created_at" ]
}
Use this endpoint to generate TypeScript types or validate data client-side.
Real-time Subscriptions
Subscription System Server-Sent Events (SSE) for real-time record updates.
Endpoint : GET /api/records/v1/{api_name}/subscribe/{record_id}
const eventSource = new EventSource (
`http://localhost:4000/api/records/v1/posts/subscribe/123` ,
{ withCredentials: true }
);
eventSource . onmessage = ( event ) => {
const record = JSON . parse ( event . data );
console . log ( 'Record updated:' , record );
};
eventSource . onerror = ( error ) => {
console . error ( 'Subscription error:' , error );
eventSource . close ();
};
How it works :
Client establishes SSE connection
Server creates SQLite trigger for the record
On record update, trigger fires
SubscriptionManager notifies active subscriptions
Server sends updated record as SSE event
Subscriptions must be enabled in the Record API config: enable_subscriptions: true
File Upload & Download
Upload File
Files are uploaded as base64-encoded JSON:
curl -X POST http://localhost:4000/api/records/v1/users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_token>" \
-d '{
"email": "[email protected] ",
"avatar": {
"data": "<base64_encoded_image>",
"mimeType": "image/jpeg",
"name": "avatar.jpg"
}
}'
Download File
Endpoint : GET /api/records/v1/{api_name}/{record_id}/file/{column_name}
curl http://localhost:4000/api/records/v1/users/123/file/avatar \
-o avatar.jpg
Files are served with proper MIME types and content-disposition headers.
Transactions
Transaction API Batch multiple operations in a single atomic transaction.
Endpoint : POST /api/transaction/v1/execute
curl -X POST http://localhost:4000/api/transaction/v1/execute \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <auth_token>" \
-d '{
"operations": [
{
"api_name": "posts",
"operation": "create",
"data": { "title": "Post 1", "content": "Content 1" }
},
{
"api_name": "posts",
"operation": "create",
"data": { "title": "Post 2", "content": "Content 2" }
}
]
}'
Response :
{
"results" : [
{ "id" : 123 , "title" : "Post 1" , "content" : "Content 1" },
{ "id" : 124 , "title" : "Post 2" , "content" : "Content 2" }
]
}
Transactions must be explicitly enabled: server.enable_record_transactions: true
Auth APIs
Auth Router Complete authentication API including registration, login, and OAuth.
Auth APIs are versioned at /api/auth/v1:
Register
POST /api/auth/v1/register
Content-Type: application/json
{
"email" : "[email protected] ",
"password" : "secure_password123"
}
Login
POST /api/auth/v1/login
Content-Type: application/json
{
"email" : "[email protected] ",
"password" : "secure_password123"
}
Response :
{
"auth_token" : "eyJhbGc..." ,
"refresh_token" : "..." ,
"user_id" : "base64_user_id" ,
"expires_in" : 3600
}
Refresh Token
POST /api/auth/v1/refresh
Cookie: refresh_token= < toke n >
Login Status
GET /api/auth/v1/status
Authorization: Bearer < auth_toke n >
Logout
GET /api/auth/v1/logout
Authorization: Bearer < auth_toke n >
OAuth
# Start OAuth flow
GET /api/auth/v1/oauth/login/{provider}?redirect_uri= < ur i >& code_challenge= < pkc e >
# OAuth callback (handled by provider)
GET /api/auth/v1/oauth/callback/{provider}?code= < cod e >& state= < stat e >
# Exchange code for tokens
POST /api/auth/v1/token
{
"code" : "<auth_code>",
"code_verifier" : "<pkce_verifier>"
}
Admin APIs
Admin APIs require authentication as an admin user and CSRF token validation.
Admin APIs are at /_/admin/api/ and include:
User management
Table operations (create, alter, drop)
Index management
Configuration updates
System logs
Job management
OpenAPI Documentation
TrailBase generates OpenAPI specs:
Endpoint : GET /api/docs/openapi.json
// From lib.rs:117-133
#[derive( OpenApi )]
#[openapi(
info(
title = "TrailBase" ,
description = "TrailBase APIs" ,
),
nest(
(path = "/api/auth/v1" , api = crate :: auth :: AuthApi ),
(path = "/api/records/v1" , api = crate :: records :: RecordOpenApi ),
),
)]
pub struct Doc ;
Use tools like Swagger UI or Redoc to view the interactive API documentation.
Type Safety
TypeScript Generation
Generate TypeScript types from JSON schemas:
# Fetch schema
curl http://localhost:4000/api/records/v1/posts/schema > post-schema.json
# Use json-schema-to-typescript
npx json-schema-to-typescript post-schema.json > post.types.ts
Generated types:
export interface Post {
id : number ;
title : string ;
content ?: string | null ;
user_id : string ;
published : boolean ;
created_at : number ;
}
Client Libraries
Create type-safe API clients:
class PostsAPI {
private baseUrl = 'http://localhost:4000/api/records/v1/posts' ;
async create ( data : Omit < Post , 'id' | 'created_at' >) : Promise < Post > {
const response = await fetch ( this . baseUrl , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( data ),
});
return response . json ();
}
async get ( id : number ) : Promise < Post > {
const response = await fetch ( ` ${ this . baseUrl } / ${ id } ` );
return response . json ();
}
async list ( params ?: {
filter ?: string ;
limit ?: number ;
offset ?: number ;
}) : Promise <{ records : Post []; total_count ?: number }> {
const query = new URLSearchParams ( params ). toString ();
const response = await fetch ( ` ${ this . baseUrl } ? ${ query } ` );
return response . json ();
}
}
Error Handling
API errors follow standard HTTP status codes:
Status Meaning Example 400 Bad Request Invalid JSON or schema validation failure 401 Unauthorized Missing or invalid auth token 403 Forbidden Access denied by ACL or access rule 404 Not Found Record or API doesn’t exist 409 Conflict Unique constraint violation 429 Too Many Requests Rate limit exceeded 500 Internal Server Error Server-side error
Error Response Format :
{
"error" : "Validation failed" ,
"details" : "Field 'email' must be a valid email address"
}
Rate Limiting
API rate limits:
Auth Endpoints : 5 requests per 10 seconds (POST only)
Record APIs : No default limit (configure per-API if needed)
Key : Client IP address
Rate limit headers:
x-ratelimit-limit: 5
x-ratelimit-remaining: 4
x-ratelimit-reset: 1704067200
Best Practices
Use Pagination Always set limit to avoid large responses
Validate Client-Side Use JSON schemas for immediate feedback
Handle Errors Check status codes and parse error messages
Cache Schemas Schema endpoints can be cached client-side
Next Steps
Authentication Learn how to authenticate API requests
Architecture Understand how APIs are generated and served