> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/trailbaseio/trailbase/llms.txt
> Use this file to discover all available pages before exploring further.

# APIs

> REST API structure, record APIs, type-safe APIs, auto-generated schemas, and API versioning

# 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:

<CardGroup cols={3}>
  <Card title="Record APIs" icon="database">
    Auto-generated CRUD APIs for tables/views
  </Card>

  <Card title="Auth APIs" icon="lock">
    User authentication and session management
  </Card>

  <Card title="Admin APIs" icon="shield">
    System administration and configuration
  </Card>
</CardGroup>

## 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

<Card title="Record API Module" icon="layer-group" href="/home/daytona/workspace/source/crates/core/src/records/mod.rs">
  Record APIs provide CRUD operations with automatic type validation and access control.
</Card>

### Endpoint Structure

All record API endpoints follow this pattern:

```
/api/records/v1/{api_name}/{operation}
```

From `records/mod.rs:50-97`, the router defines:

```rust theme={null}
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}`

```bash theme={null}
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**:

```json theme={null}
{
  "id": 123,
  "title": "My First Post",
  "content": "Hello, world!",
  "user_id": { "id": "base64_user_id" },
  "published": false,
  "created_at": 1704067200000
}
```

<Info>
  Implemented in `records/create_record.rs`. Fields are validated against the table's JSON schema.
</Info>

### Read Record

**Endpoint**: `GET /api/records/v1/{api_name}/{record_id}`

```bash theme={null}
curl http://localhost:4000/api/records/v1/posts/123
```

**Query Parameters**:

* `expand`: Comma-separated foreign keys to expand

```bash theme={null}
# With foreign key expansion
curl "http://localhost:4000/api/records/v1/posts/123?expand=user_id,category_id"
```

**Response** (with expansion):

```json theme={null}
{
  "id": 123,
  "title": "My First Post",
  "content": "Hello, world!",
  "user_id": {
    "id": "base64_user_id",
    "data": {
      "id": "base64_user_id",
      "email": "user@example.com",
      "verified": true
    }
  },
  "published": false,
  "created_at": 1704067200000
}
```

<Card title="Read Implementation" icon="book-open" href="/home/daytona/workspace/source/crates/core/src/records/read_record.rs">
  Handles record fetching, foreign key expansion, and access control.
</Card>

### Update Record

**Endpoint**: `PATCH /api/records/v1/{api_name}/{record_id}`

```bash theme={null}
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

<Info>
  Implemented in `records/update_record.rs` with conflict resolution support.
</Info>

### Delete Record

**Endpoint**: `DELETE /api/records/v1/{api_name}/{record_id}`

```bash theme={null}
curl -X DELETE http://localhost:4000/api/records/v1/posts/123 \
  -H "Authorization: Bearer <auth_token>"
```

**Response**: `204 No Content` on success

<Warning>
  Deletions are permanent and cannot be undone. Implement soft deletes if needed.
</Warning>

### List Records

<Card title="List Implementation" icon="list" href="/home/daytona/workspace/source/crates/core/src/records/list_records.rs">
  Provides filtering, sorting, pagination, and aggregation.
</Card>

**Endpoint**: `GET /api/records/v1/{api_name}`

**Query Parameters**:

| Parameter | Type    | Description                 |
| --------- | ------- | --------------------------- |
| `filter`  | string  | SQL WHERE clause expression |
| `order`   | string  | Column to sort by           |
| `limit`   | integer | Max records to return       |
| `offset`  | integer | Number of records to skip   |
| `count`   | boolean | Include total count         |
| `expand`  | string  | Foreign keys to expand      |

**Example**:

```bash theme={null}
curl "http://localhost:4000/api/records/v1/posts?filter=published%3D1&order=created_at%20DESC&limit=10&count=true"
```

**Response**:

```json theme={null}
{
  "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:

```bash theme={null}
# 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)
```

<Warning>
  Filter expressions are validated and sanitized to prevent SQL injection.
</Warning>

### JSON Schema Endpoint

**Endpoint**: `GET /api/records/v1/{api_name}/schema`

```bash theme={null}
curl http://localhost:4000/api/records/v1/posts/schema
```

**Response**:

```json theme={null}
{
  "$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"]
}
```

<Info>
  Use this endpoint to generate TypeScript types or validate data client-side.
</Info>

## Real-time Subscriptions

<Card title="Subscription System" icon="satellite-dish" href="/home/daytona/workspace/source/crates/core/src/records/subscribe.rs">
  Server-Sent Events (SSE) for real-time record updates.
</Card>

**Endpoint**: `GET /api/records/v1/{api_name}/subscribe/{record_id}`

```typescript theme={null}
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**:

1. Client establishes SSE connection
2. Server creates SQLite trigger for the record
3. On record update, trigger fires
4. `SubscriptionManager` notifies active subscriptions
5. Server sends updated record as SSE event

<Info>
  Subscriptions must be enabled in the Record API config: `enable_subscriptions: true`
</Info>

## File Upload & Download

### Upload File

Files are uploaded as base64-encoded JSON:

```bash theme={null}
curl -X POST http://localhost:4000/api/records/v1/users \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <auth_token>" \
  -d '{
    "email": "user@example.com",
    "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}`

```bash theme={null}
curl http://localhost:4000/api/records/v1/users/123/file/avatar \
  -o avatar.jpg
```

<Info>
  Files are served with proper MIME types and content-disposition headers.
</Info>

## Transactions

<Card title="Transaction API" icon="arrows-spin" href="/home/daytona/workspace/source/crates/core/src/records/transaction.rs">
  Batch multiple operations in a single atomic transaction.
</Card>

**Endpoint**: `POST /api/transaction/v1/execute`

```bash theme={null}
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**:

```json theme={null}
{
  "results": [
    { "id": 123, "title": "Post 1", "content": "Content 1" },
    { "id": 124, "title": "Post 2", "content": "Content 2" }
  ]
}
```

<Warning>
  Transactions must be explicitly enabled: `server.enable_record_transactions: true`
</Warning>

## Auth APIs

<Card title="Auth Router" icon="right-to-bracket" href="/home/daytona/workspace/source/crates/core/src/auth/mod.rs:67-173">
  Complete authentication API including registration, login, and OAuth.
</Card>

Auth APIs are versioned at `/api/auth/v1`:

### Register

```bash theme={null}
POST /api/auth/v1/register
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure_password123"
}
```

### Login

```bash theme={null}
POST /api/auth/v1/login
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure_password123"
}
```

**Response**:

```json theme={null}
{
  "auth_token": "eyJhbGc...",
  "refresh_token": "...",
  "user_id": "base64_user_id",
  "expires_in": 3600
}
```

### Refresh Token

```bash theme={null}
POST /api/auth/v1/refresh
Cookie: refresh_token=<token>
```

### Login Status

```bash theme={null}
GET /api/auth/v1/status
Authorization: Bearer <auth_token>
```

### Logout

```bash theme={null}
GET /api/auth/v1/logout
Authorization: Bearer <auth_token>
```

### OAuth

```bash theme={null}
# Start OAuth flow
GET /api/auth/v1/oauth/login/{provider}?redirect_uri=<uri>&code_challenge=<pkce>

# OAuth callback (handled by provider)
GET /api/auth/v1/oauth/callback/{provider}?code=<code>&state=<state>

# Exchange code for tokens
POST /api/auth/v1/token
{
  "code": "<auth_code>",
  "code_verifier": "<pkce_verifier>"
}
```

## Admin APIs

<Warning>
  Admin APIs require authentication as an admin user and CSRF token validation.
</Warning>

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`

```rust theme={null}
// 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;
```

<Info>
  Use tools like Swagger UI or Redoc to view the interactive API documentation.
</Info>

## Type Safety

### TypeScript Generation

Generate TypeScript types from JSON schemas:

```bash theme={null}
# 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:

```typescript theme={null}
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:

```typescript theme={null}
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**:

```json theme={null}
{
  "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

<CardGroup cols={2}>
  <Card title="Use Pagination" icon="list-ol">
    Always set `limit` to avoid large responses
  </Card>

  <Card title="Validate Client-Side" icon="check">
    Use JSON schemas for immediate feedback
  </Card>

  <Card title="Handle Errors" icon="triangle-exclamation">
    Check status codes and parse error messages
  </Card>

  <Card title="Cache Schemas" icon="floppy-disk">
    Schema endpoints can be cached client-side
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Authentication" icon="shield" href="./authentication">
    Learn how to authenticate API requests
  </Card>

  <Card title="Architecture" icon="diagram-project" href="./architecture">
    Understand how APIs are generated and served
  </Card>
</CardGroup>
