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

# OpenAPI Specification

> Automatic OpenAPI documentation and Swagger UI for your APIs

## Overview

TrailBase automatically generates OpenAPI 3.0 specifications for your APIs, providing interactive documentation through Swagger UI and enabling API client generation.

## Accessing API Documentation

### Swagger UI

Access interactive API documentation at:

```
https://yourdomain.com/_/docs
```

The Swagger UI provides:

* **Interactive testing**: Try API endpoints directly from the browser
* **Request/response examples**: See sample data for each endpoint
* **Authentication**: Test authenticated endpoints
* **Schema documentation**: Explore data models and types

### OpenAPI JSON

Download the raw OpenAPI specification:

```bash theme={null}
curl https://yourdomain.com/openapi.json > api-spec.json
```

## API Structure

From `crates/core/src/lib.rs`:

```rust theme={null}
use utoipa::OpenApi;

#[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),
    ),
    tags(),
)]
pub struct Doc;
```

## API Categories

### Authentication API

Endpoints under `/api/auth/v1`:

* `POST /register` - Register new user
* `POST /login` - Login with email/password
* `POST /logout` - Logout current user
* `GET /user` - Get current user profile
* `POST /verify_email` - Request email verification
* `POST /reset_password` - Request password reset
* `GET /oauth/{provider}` - OAuth login
* `GET /oauth/{provider}/callback` - OAuth callback
* `GET /oauth/providers` - List OAuth providers

### Records API

Endpoints under `/api/records/v1/{table}`:

* `GET /{table}` - List records
* `POST /{table}` - Create record
* `GET /{table}/{id}` - Get record by ID
* `PATCH /{table}/{id}` - Update record
* `DELETE /{table}/{id}` - Delete record
* `GET /files/{id}` - Download file

## Custom API Documentation

### Document Custom Endpoints

Add OpenAPI annotations to your endpoints:

```rust theme={null}
use utoipa::{OpenApi, IntoParams, ToSchema};

#[derive(Debug, Deserialize, IntoParams)]
struct SearchQuery {
    /// Search query string
    #[param(example = "rust")]
    q: String,
    
    /// Maximum number of results
    #[param(minimum = 1, maximum = 100, default = 10)]
    limit: Option<i32>,
}

#[derive(Debug, Serialize, ToSchema)]
struct SearchResult {
    /// Unique identifier
    id: i64,
    
    /// Result title
    title: String,
    
    /// Relevance score (0-1)
    #[schema(minimum = 0, maximum = 1)]
    score: f64,
}

/// Search for items
#[utoipa::path(
    get,
    path = "/search",
    params(SearchQuery),
    responses(
        (status = 200, description = "Search results", body = Vec<SearchResult>),
        (status = 400, description = "Invalid query"),
    ),
    tag = "search"
)]
async fn search(Query(params): Query<SearchQuery>) -> Json<Vec<SearchResult>> {
    // Implementation
}
```

### Group Custom APIs

```rust theme={null}
#[derive(OpenApi)]
#[openapi(
    paths(
        search,
        get_recommendations,
        get_trending,
    ),
    components(
        schemas(SearchResult, Recommendation, TrendingItem)
    ),
    tags(
        (name = "search", description = "Search operations"),
        (name = "discovery", description = "Content discovery")
    )
)]
struct CustomApi;
```

## Schema Generation

### Derive Schema

```rust theme={null}
use utoipa::ToSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, ToSchema)]
#[schema(example = json!({
    "id": 1,
    "title": "Example Post",
    "published": true
}))]
struct Post {
    /// Unique post identifier
    #[schema(minimum = 1)]
    id: i64,
    
    /// Post title
    #[schema(min_length = 1, max_length = 200)]
    title: String,
    
    /// Post content in Markdown
    content: String,
    
    /// Whether the post is published
    #[schema(default = false)]
    published: bool,
    
    /// Publication date
    #[schema(value_type = String, format = "date-time")]
    published_at: Option<chrono::DateTime<chrono::Utc>>,
    
    /// Post tags
    #[schema(example = json!(["rust", "api"]))]
    tags: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize, ToSchema)]
enum PostStatus {
    /// Post is in draft state
    Draft,
    /// Post is published and visible
    Published,
    /// Post is archived
    Archived,
}
```

### Nested Schemas

```rust theme={null}
#[derive(Debug, Serialize, ToSchema)]
struct Author {
    id: i64,
    name: String,
    email: String,
}

#[derive(Debug, Serialize, ToSchema)]
struct PostWithAuthor {
    #[serde(flatten)]
    post: Post,
    
    /// Post author
    author: Author,
}
```

## Response Documentation

### Success Responses

```rust theme={null}
#[utoipa::path(
    get,
    path = "/posts/{id}",
    responses(
        (status = 200, description = "Post found", body = Post),
        (status = 404, description = "Post not found"),
        (status = 401, description = "Unauthorized"),
    ),
    params(
        ("id" = i64, Path, description = "Post ID")
    )
)]
async fn get_post(Path(id): Path<i64>) -> Result<Json<Post>, StatusCode> {
    // Implementation
}
```

### Error Responses

```rust theme={null}
#[derive(Debug, Serialize, ToSchema)]
struct ErrorResponse {
    /// Error message
    error: String,
    
    /// Optional error details
    details: Option<serde_json::Value>,
}

#[utoipa::path(
    post,
    path = "/posts",
    request_body = Post,
    responses(
        (status = 201, description = "Post created", body = Post),
        (status = 400, description = "Invalid input", body = ErrorResponse),
        (status = 401, description = "Unauthorized"),
    )
)]
async fn create_post(Json(post): Json<Post>) -> Result<Json<Post>, (StatusCode, Json<ErrorResponse>)> {
    // Implementation
}
```

## Authentication Documentation

### Bearer Token

```rust theme={null}
use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme};

#[derive(OpenApi)]
#[openapi(
    modifiers(&SecurityAddon),
)]
struct ApiDoc;

struct SecurityAddon;

impl utoipa::Modify for SecurityAddon {
    fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) {
        if let Some(components) = openapi.components.as_mut() {
            components.add_security_scheme(
                "bearer_auth",
                SecurityScheme::Http(
                    HttpBuilder::new()
                        .scheme(HttpAuthScheme::Bearer)
                        .bearer_format("JWT")
                        .build()
                ),
            )
        }
    }
}

#[utoipa::path(
    get,
    path = "/protected",
    responses(
        (status = 200, description = "Success"),
    ),
    security(
        ("bearer_auth" = [])
    )
)]
async fn protected_endpoint() -> String {
    "Protected data".to_string()
}
```

## Client Generation

### Generate TypeScript Client

Use [openapi-typescript](https://github.com/drwpow/openapi-typescript):

```bash theme={null}
# Install
npm install -D openapi-typescript

# Generate types
npx openapi-typescript https://yourdomain.com/openapi.json -o ./src/api-types.ts
```

Usage:

```typescript theme={null}
import type { paths } from './api-types';

type SearchParams = paths['/api/search']['get']['parameters']['query'];
type SearchResponse = paths['/api/search']['get']['responses']['200']['content']['application/json'];

async function search(params: SearchParams): Promise<SearchResponse> {
  const query = new URLSearchParams(params as any);
  const response = await fetch(`/api/search?${query}`);
  return response.json();
}
```

### Generate Python Client

Use [openapi-python-client](https://github.com/openapi-generators/openapi-python-client):

```bash theme={null}
pip install openapi-python-client

openapi-python-client generate --url https://yourdomain.com/openapi.json
```

### Generate Go Client

Use [oapi-codegen](https://github.com/deepmap/oapi-codegen):

```bash theme={null}
go install github.com/deepmap/oapi-codegen/cmd/oapi-codegen@latest

oapi-codegen -package api https://yourdomain.com/openapi.json > api/client.go
```

## Testing with OpenAPI

### Validate Responses

```rust theme={null}
#[cfg(test)]
mod tests {
    use super::*;
    
    #[tokio::test]
    async fn test_openapi_spec() {
        let spec = ApiDoc::openapi();
        
        // Validate spec is valid OpenAPI
        assert!(spec.info.title == "TrailBase");
        assert!(!spec.paths.paths.is_empty());
        
        // Check all endpoints have responses
        for (path, item) in spec.paths.paths.iter() {
            if let Some(operation) = &item.get {
                assert!(
                    !operation.responses.responses.is_empty(),
                    "GET {} missing responses",
                    path
                );
            }
        }
    }
}
```

### Contract Testing

```typescript theme={null}
import { test, expect } from '@jest/globals';
import OpenAPIValidator from 'openapi-validator';

test('API responses match OpenAPI spec', async () => {
  const spec = await fetch('https://api.example.com/openapi.json').then(r => r.json());
  const validator = new OpenAPIValidator(spec);
  
  const response = await fetch('https://api.example.com/api/records/v1/posts');
  const body = await response.json();
  
  const validation = validator.validate('/api/records/v1/posts', 'get', response.status, body);
  expect(validation.errors).toHaveLength(0);
});
```

## Best Practices

<Steps>
  <Step title="Document all endpoints">
    Add OpenAPI annotations to every public endpoint for complete documentation.
  </Step>

  <Step title="Provide examples">
    Include example requests and responses:

    ```rust theme={null}
    #[schema(example = json!({
        "id": 1,
        "name": "Example"
    }))]
    ```
  </Step>

  <Step title="Use descriptive tags">
    Group related endpoints with tags:

    ```rust theme={null}
    #[utoipa::path(
        tag = "users",
        // ...
    )]
    ```
  </Step>

  <Step title="Document errors">
    Specify all possible error responses with status codes and error schemas.
  </Step>

  <Step title="Keep specs in sync">
    Run validation tests to ensure OpenAPI specs match implementation.
  </Step>

  <Step title="Version your APIs">
    Use path prefixes for API versioning:

    ```rust theme={null}
    nest(
        (path = "/api/v1", api = ApiV1),
        (path = "/api/v2", api = ApiV2),
    )
    ```
  </Step>
</Steps>

## Customizing Swagger UI

The Swagger UI can be customized by modifying the HTML template in the TrailBase source:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <title>API Documentation</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui.css">
  <style>
    /* Custom styles */
    .swagger-ui .topbar { display: none; }
  </style>
</head>
<body>
  <div id="swagger-ui"></div>
  <script src="https://cdn.jsdelivr.net/npm/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
  <script>
    SwaggerUIBundle({
      url: '/openapi.json',
      dom_id: '#swagger-ui',
      deepLinking: true,
      presets: [
        SwaggerUIBundle.presets.apis,
        SwaggerUIBundle.SwaggerUIStandalonePreset
      ],
    });
  </script>
</body>
</html>
```

## Alternative API Documentation

### Redoc

Use [Redoc](https://github.com/Redocly/redoc) for a different documentation style:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <title>API Reference</title>
  <script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
</head>
<body>
  <redoc spec-url="/openapi.json"></redoc>
</body>
</html>
```

### RapiDoc

[RapiDoc](https://rapidocweb.com/) offers a customizable API console:

```html theme={null}
<!DOCTYPE html>
<html>
<head>
  <script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body>
  <rapi-doc
    spec-url="/openapi.json"
    theme="dark"
    render-style="view"
  ></rapi-doc>
</body>
</html>
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Custom Endpoints" href="/advanced/custom-endpoints" icon="route">
    Build documented APIs
  </Card>

  <Card title="WASM Components" href="/advanced/wasm-components" icon="cube">
    Create custom endpoints
  </Card>

  <Card title="OAuth Providers" href="/advanced/oauth-providers" icon="shield">
    Document auth flows
  </Card>

  <Card title="Object Storage" href="/advanced/object-storage" icon="database">
    File upload APIs
  </Card>
</CardGroup>
