---
title: "HeliosDB Nano REST API Tutorial"
---

# HeliosDB Nano REST API Tutorial


---

## Table of Contents

1. [Introduction](#1-introduction)
2. [Setup](#2-setup)
3. [Authentication](#3-authentication)
4. [Branch Operations](#4-branch-operations)
5. [Data Operations](#5-data-operations)
6. [SQL Execution](#6-sql-execution)
7. [Time-Travel via API](#7-time-travel-via-api)
8. [Error Handling](#8-error-handling)
9. [SDK Examples](#9-sdk-examples)
10. [Rate Limiting](#10-rate-limiting)

---

## 1. Introduction

### REST API Overview

HeliosDB Nano provides a RESTful HTTP API for database operations, enabling you to interact with your database through standard HTTP requests. The API supports:

- Branch management (create, list, merge, delete)
- SQL query execution
- Data CRUD operations
- Time-travel queries
- Authentication and authorization
- Rate limiting

### When to Use the API

Use the REST API when you need to:

- **Access HeliosDB from any programming language** - HTTP clients are available in all major languages
- **Build web applications** - Integrate directly with frontend frameworks
- **Implement microservices** - Decouple database logic from application services
- **Automate database operations** - Script common tasks using curl or HTTP clients
- **Integrate with external tools** - Connect BI tools, data pipelines, or monitoring systems

The REST API is ideal for stateless, request-response patterns. For persistent connections or PostgreSQL compatibility, consider the PostgreSQL wire protocol instead.

### Base URL Configuration

The API base URL depends on your deployment:

```
Development:  http://localhost:8080/v1
Production:   https://your-domain.com/v1
Custom:       http://<host>:<port>/v1
```

All endpoints are versioned under `/v1` to ensure backward compatibility.

---

## 2. Setup

### Starting the API Server

#### Basic Server Setup

```rust
use heliosdb_nano::{EmbeddedDatabase, api::ApiServer};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create database instance
    let db = Arc::new(EmbeddedDatabase::new("./data/mydb.db")?);

    // Configure server address
    let addr = "127.0.0.1:8080".parse()?;

    // Create and start API server
    let server = ApiServer::new(addr, db);
    server.serve().await?;

    Ok(())
}
```

#### Server with Authentication

```rust
use heliosdb_nano::{
    EmbeddedDatabase,
    api::{ApiServer, AuthMiddleware},
};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Arc::new(EmbeddedDatabase::new("./data/mydb.db")?);
    let addr = "127.0.0.1:8080".parse()?;

    // Configure authentication
    let auth = AuthMiddleware::from_env_or_default();

    let server = ApiServer::new(addr, db)
        .with_auth(auth);

    server.serve().await?;

    Ok(())
}
```

#### Server with Rate Limiting

```rust
use heliosdb_nano::{
    EmbeddedDatabase,
    api::{ApiServer, RateLimitMiddleware, RateLimitConfig},
};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Arc::new(EmbeddedDatabase::new("./data/mydb.db")?);
    let addr = "127.0.0.1:8080".parse()?;

    // Configure rate limiting: 100 requests per minute
    let rate_limiter = RateLimitMiddleware::new(
        RateLimitConfig::authenticated()
    );

    let server = ApiServer::new(addr, db)
        .with_rate_limiting(rate_limiter);

    server.serve().await?;

    Ok(())
}
```

#### Server with Graceful Shutdown

```rust
use heliosdb_nano::{EmbeddedDatabase, api::ApiServer};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let db = Arc::new(EmbeddedDatabase::new("./data/mydb.db")?);
    let addr = "127.0.0.1:8080".parse()?;

    let server = ApiServer::new(addr, db);

    // Shutdown on Ctrl+C
    server.serve_with_shutdown(async {
        tokio::signal::ctrl_c()
            .await
            .expect("Failed to listen for Ctrl+C");
        println!("Shutting down gracefully...");
    }).await?;

    Ok(())
}
```

### Configuration Options

#### Environment Variables

```bash
# JWT Secret (required for authentication)
export HELIOSDB_JWT_SECRET="your-secret-key-change-in-production"

# Server configuration
export HELIOSDB_API_HOST="0.0.0.0"
export HELIOSDB_API_PORT="8080"

# Rate limiting
export HELIOSDB_RATE_LIMIT_MAX_REQUESTS="100"
export HELIOSDB_RATE_LIMIT_WINDOW_SECS="60"
```

#### Rate Limit Configurations

HeliosDB Nano provides predefined rate limit configurations:

| Configuration | Requests/Minute | Burst Capacity | Use Case |
|---------------|-----------------|----------------|----------|
| `public()` | 30 | 5 | Public endpoints |
| `authenticated()` | 100 | 10 | Standard authenticated users |
| `heavy_operations()` | 10 | 2 | Heavy queries/operations |
| Custom | Configurable | Configurable | Custom requirements |

```rust
// Custom rate limit
let config = RateLimitConfig::new(
    150,  // max requests
    60,   // window in seconds
    15    // burst capacity
);
```

### Health Check Endpoints

Verify Server state with built-in health endpoints:

```bash
# Health check
curl http://localhost:8080/health

# Response: "OK" with 200 status

# Version information
curl http://localhost:8080/version

# Response:
# {
#   "name": "HeliosDB Nano",
#   "version": "<version>",
#   "api_version": "v1"
# }
```

---

## 3. Authentication

:::caution[Authentication on the stock server is planned]
API key and JWT authentication for the HTTP API of `heliosdb-nano start` are
planned. Keep the HTTP and MySQL listeners on loopback (the default): API-key
authentication for the HTTP API is planned, and a smaller listening surface is
good practice anyway. The middleware in this section applies when you embed the API server in your own
application and attach it with `ApiServer::with_auth`.
:::

HeliosDB Nano supports two authentication methods: JWT Bearer tokens and API keys.

### JWT Authentication

JWT (JSON Web Token) is recommended for user-based authentication with time-limited sessions.

#### Token Structure

JWT tokens contain:
- **Subject (sub)**: User ID
- **Tenant ID**: For multi-tenancy
- **Client ID**: Unique client identifier
- **Expiration (exp)**: Token expiry timestamp
- **Scopes**: Permissions array

#### Generating JWT Tokens

```rust
use heliosdb_nano::sync::auth::{JwtManager, Claims};
use chrono::Duration;
use uuid::Uuid;

fn generate_token() -> String {
    let jwt_manager = JwtManager::new(b"your-secret-key");

    jwt_manager.generate_token(
        "user123".to_string(),      // user_id
        "tenant456".to_string(),     // tenant_id
        Uuid::new_v4(),              // client_id
    ).unwrap()
}
```

#### Using JWT in Requests

Include the JWT token in the `Authorization` header:

```bash
curl -H "Authorization: Bearer <your-jwt-token>" \
  http://localhost:8080/v1/branches
```

#### Token Scopes

Default scopes:
- `sync:read` - Read operations
- `sync:write` - Write operations

Custom scopes can be added when creating Claims.

### API Key Authentication

API keys provide simple, long-lived authentication ideal for service accounts and automation.

#### Creating an API Key

```rust
use heliosdb_nano::api::AuthMiddleware;
use uuid::Uuid;

let auth = AuthMiddleware::new(b"jwt-secret")
    .with_api_key(
        "sk_test_abc123xyz".to_string(),  // API key
        "user123".to_string(),             // user_id
        "tenant456".to_string(),           // tenant_id
        Uuid::new_v4(),                    // client_id
        vec![                              // scopes
            "read".to_string(),
            "write".to_string()
        ],
        "Production API Key".to_string()   // name/description
    );
```

#### Using API Keys in Requests

Include the API key in the `X-API-Key` header:

```bash
curl -H "X-API-Key: sk_test_abc123xyz" \
  http://localhost:8080/v1/branches
```

### Authentication Priority

When both authentication methods are provided:
1. JWT Bearer token is checked first
2. If JWT fails or is missing, API key is checked
3. If both fail, request is rejected with 401 Unauthorized

### Authentication Errors

```json
{
  "status": 401,
  "error": "Unauthorized",
  "message": "Missing or invalid authentication credentials"
}
```

Common authentication errors:
- **401 Unauthorized**: Missing or invalid credentials
- **403 Forbidden**: Insufficient permissions (valid auth, wrong scopes)

---

## 4. Branch Operations

Branch operations allow you to create, manage, and merge isolated database environments.

### List Branches

Retrieve all branches in the database.

**Endpoint**: `GET /v1/branches`

**Request**:
```bash
curl -H "Authorization: Bearer <token>" \
  http://localhost:8080/v1/branches
```

**Response** (200 OK):
```json
{
  "total": 3,
  "branches": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "main",
      "parent": null,
      "created_at": 1701446400,
      "snapshot_id": 1701446400,
      "state": "Active",
      "stats": {
        "total_keys": 1250,
        "size_bytes": 524288,
        "last_modified": 1701446400
      }
    },
    {
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "name": "development",
      "parent": "main",
      "created_at": 1701450000,
      "snapshot_id": 1701450000,
      "state": "Active",
      "stats": {
        "total_keys": 1275,
        "size_bytes": 535552,
        "last_modified": 1701452000
      }
    }
  ]
}
```

### Create Branch

Create a new branch from an existing branch or snapshot.

**Endpoint**: `POST /v1/branches`

**Request Body**:
```json
{
  "name": "feature-user-auth",
  "parent": "main",
  "snapshot_id": 1701446400,
  "options": {
    "read_only": false,
    "isolated": true
  }
}
```

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "feature-user-auth",
    "parent": "main"
  }' \
  http://localhost:8080/v1/branches
```

**Response** (201 Created):
```json
{
  "id": "770e8400-e29b-41d4-a716-446655440002",
  "name": "feature-user-auth",
  "parent": "main",
  "created_at": 1701455000,
  "snapshot_id": 1701446400,
  "state": "Active",
  "stats": {
    "total_keys": 1250,
    "size_bytes": 524288,
    "last_modified": 1701455000
  }
}
```

**Parameters**:
- `name` (required): Branch name (alphanumeric, hyphens, underscores)
- `parent` (optional): Parent branch name (defaults to "main")
- `snapshot_id` (optional): Timestamp to branch from (defaults to current)
- `options` (optional): Branch configuration

### Get Branch Details

Retrieve detailed information about a specific branch.

**Endpoint**: `GET /v1/branches/{name}`

**Request**:
```bash
curl -H "Authorization: Bearer <token>" \
  http://localhost:8080/v1/branches/feature-user-auth
```

**Response** (200 OK):
```json
{
  "id": "770e8400-e29b-41d4-a716-446655440002",
  "name": "feature-user-auth",
  "parent": "main",
  "created_at": 1701455000,
  "snapshot_id": 1701446400,
  "state": "Active",
  "stats": {
    "total_keys": 1280,
    "size_bytes": 540672,
    "last_modified": 1701460000
  }
}
```

**Errors**:
- `404 Not Found`: Branch does not exist

### Delete Branch

Delete a branch. Cannot delete the main branch or branches with children.

**Endpoint**: `DELETE /v1/branches/{name}`

**Request**:
```bash
curl -X DELETE \
  -H "Authorization: Bearer <token>" \
  http://localhost:8080/v1/branches/feature-user-auth
```

**Response** (204 No Content):
Empty body with status 204

**Errors**:
- `400 Bad Request`: Cannot delete main branch or branch has children
- `404 Not Found`: Branch does not exist

### Merge Branches

Merge changes from one branch into another.

**Endpoint**: `POST /v1/branches/{source}/merge`

**Request Body**:
```json
{
  "target": "main",
  "strategy": "Auto"
}
```

**Merge Strategies**:
- `Auto`: Automatically resolve conflicts (prefer newer values)
- `Ours`: Always keep target branch values
- `Theirs`: Always use source branch values
- `Manual`: Fail on conflicts (require manual resolution)

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "target": "main",
    "strategy": "Auto"
  }' \
  http://localhost:8080/v1/branches/feature-user-auth/merge
```

**Response** (200 OK):
```json
{
  "merge_timestamp": 1701465000,
  "merged_keys": 30,
  "conflicts": [],
  "completed": true,
  "message": "Successfully merged 30 keys from 'feature-user-auth' into 'main' with no conflicts"
}
```

**Response with Conflicts** (200 OK):
```json
{
  "merge_timestamp": 1701465000,
  "merged_keys": 28,
  "conflicts": [
    {
      "key": "users:123",
      "source_value": "Alice Smith",
      "target_value": "Alice Johnson",
      "resolution": "Auto",
      "resolved_value": "Alice Smith"
    }
  ],
  "completed": true,
  "message": "Successfully merged 28 keys from 'feature-user-auth' into 'main' with 2 conflicts resolved"
}
```

**Errors**:
- `404 Not Found`: Source or target branch not found
- `409 Conflict`: Unresolved conflicts (when strategy is "Manual")
- `422 Unprocessable Entity`: Invalid merge state

---

## 5. Data Operations

Data operations provide high-level CRUD functionality for working with table data.

### List Tables

List all tables in a branch.

**Endpoint**: `GET /v1/branches/{name}/tables`

**Request**:
```bash
curl -H "Authorization: Bearer <token>" \
  http://localhost:8080/v1/branches/main/tables
```

**Response** (200 OK):
```json
{
  "tables": [
    {
      "name": "users",
      "row_count": 1500,
      "size_bytes": 245760,
      "created_at": 1701440000
    },
    {
      "name": "orders",
      "row_count": 5200,
      "size_bytes": 983040,
      "created_at": 1701441000
    }
  ]
}
```

### Query Data

Retrieve data from a table with filtering, pagination, and column selection.

**Endpoint**: `GET /v1/branches/{name}/tables/{table}/data`

**Query Parameters**:
- `filter`: WHERE clause condition (e.g., `age > 25`)
- `columns`: Comma-separated column names (default: all)
- `page`: Page number, 1-based (default: 1)
- `limit`: Results per page (default: 100, max: 1000)
- `order_by`: ORDER BY clause (e.g., `created_at DESC`)
- `as_of`: Time-travel timestamp

**Request - All Data**:
```bash
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8080/v1/branches/main/tables/users/data"
```

**Request - With Filter and Pagination**:
```bash
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8080/v1/branches/main/tables/users/data?filter=age>25&page=1&limit=10"
```

**Request - Specific Columns**:
```bash
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8080/v1/branches/main/tables/users/data?columns=id,name,email"
```

**Request - With Sorting**:
```bash
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8080/v1/branches/main/tables/users/data?order_by=created_at+DESC&limit=20"
```

**Response** (200 OK):
```json
{
  "columns": ["id", "name", "email", "age"],
  "rows": [
    {
      "id": 1,
      "name": "Alice",
      "email": "alice@example.com",
      "age": 30
    },
    {
      "id": 2,
      "name": "Bob",
      "email": "bob@example.com",
      "age": 28
    }
  ],
  "total": 150,
  "page": 1,
  "limit": 10,
  "has_more": true
}
```

### Insert Data

Insert new rows into a table.

**Endpoint**: `POST /v1/branches/{name}/tables/{table}/data`

**Request Body**:
```json
{
  "rows": [
    {
      "id": 100,
      "name": "Charlie",
      "email": "charlie@example.com",
      "age": 35
    },
    {
      "id": 101,
      "name": "Diana",
      "email": "diana@example.com",
      "age": 27
    }
  ]
}
```

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "rows": [
      {
        "id": 100,
        "name": "Charlie",
        "email": "charlie@example.com"
      }
    ]
  }' \
  http://localhost:8080/v1/branches/main/tables/users/data
```

**Response** (201 Created):
```json
{
  "inserted": 1,
  "execution_time_ms": 12
}
```

### Update Data

Update existing rows in a table.

**Endpoint**: `PUT /v1/branches/{name}/tables/{table}/data`

**Request Body**:
```json
{
  "values": {
    "email": "newemail@example.com",
    "updated_at": 1701470000
  },
  "filter": "id = 100"
}
```

**Request**:
```bash
curl -X PUT \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "values": {"email": "newemail@example.com"},
    "filter": "id = 100"
  }' \
  http://localhost:8080/v1/branches/main/tables/users/data
```

**Response** (200 OK):
```json
{
  "updated": 1,
  "execution_time_ms": 8
}
```

**Warning**: Always include a `filter` to avoid updating all rows.

### Delete Data

Delete rows from a table.

**Endpoint**: `DELETE /v1/branches/{name}/tables/{table}/data`

**Request Body**:
```json
{
  "filter": "id = 100"
}
```

**Request**:
```bash
curl -X DELETE \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"filter": "id = 100"}' \
  http://localhost:8080/v1/branches/main/tables/users/data
```

**Response** (200 OK):
```json
{
  "deleted": 1,
  "execution_time_ms": 10
}
```

**Warning**: Always include a `filter` to avoid deleting all rows.

---

## 6. SQL Execution

Execute raw SQL queries and statements for maximum flexibility.

### Execute Query (SELECT)

Execute read-only SELECT queries.

**Endpoint**: `POST /v1/branches/{name}/query`

**Request Body**:
```json
{
  "sql": "SELECT * FROM users WHERE age > $1 ORDER BY created_at DESC",
  "params": [
    {
      "type": "int4",
      "value": 25
    }
  ],
  "timeout_ms": 5000
}
```

**Parameter Types**:
- `int4`: 32-bit integer
- `int8`: 64-bit integer
- `text`: String
- `bool`: Boolean
- `float4`: 32-bit float
- `float8`: 64-bit float
- `timestamp`: Unix timestamp

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT id, name, email FROM users WHERE age > $1",
    "params": [{"type": "int4", "value": 25}]
  }' \
  http://localhost:8080/v1/branches/main/query
```

**Response** (200 OK):
```json
{
  "columns": ["id", "name", "email"],
  "column_types": ["int4", "text", "text"],
  "rows": [
    {
      "id": 1,
      "name": "Alice",
      "email": "alice@example.com"
    },
    {
      "id": 3,
      "name": "Charlie",
      "email": "charlie@example.com"
    }
  ],
  "row_count": 2,
  "execution_time_ms": 42
}
```

### Execute Statement (DDL/DML)

Execute data modification statements (INSERT, UPDATE, DELETE, CREATE TABLE, etc.).

**Endpoint**: `POST /v1/branches/{name}/execute`

**Request Body**:
```json
{
  "sql": "INSERT INTO users (id, name, email) VALUES ($1, $2, $3)",
  "params": [
    {"type": "int4", "value": 200},
    {"type": "text", "value": "Eve"},
    {"type": "text", "value": "eve@example.com"}
  ],
  "timeout_ms": 5000
}
```

**Request - INSERT**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "INSERT INTO users (id, name, email) VALUES ($1, $2, $3)",
    "params": [
      {"type": "int4", "value": 200},
      {"type": "text", "value": "Eve"},
      {"type": "text", "value": "eve@example.com"}
    ]
  }' \
  http://localhost:8080/v1/branches/main/execute
```

**Response** (200 OK):
```json
{
  "statement_type": "INSERT",
  "affected_rows": 1,
  "execution_time_ms": 23,
  "message": "INSERT statement executed successfully on branch 'main'"
}
```

**Request - CREATE TABLE**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT, price REAL)"
  }' \
  http://localhost:8080/v1/branches/main/execute
```

**Response** (200 OK):
```json
{
  "statement_type": "CREATE_TABLE",
  "affected_rows": 0,
  "execution_time_ms": 15,
  "message": "CREATE_TABLE statement executed successfully on branch 'main'"
}
```

### Parameterized Queries

Always use parameterized queries to prevent SQL injection:

**Bad - SQL Injection Vulnerable**:
```json
{
  "sql": "SELECT * FROM users WHERE name = '" + userInput + "'"
}
```

**Good - Safe Parameterized Query**:
```json
{
  "sql": "SELECT * FROM users WHERE name = $1",
  "params": [{"type": "text", "value": "Alice"}]
}
```

### Query Timeout

Set timeout to prevent long-running queries:

```json
{
  "sql": "SELECT * FROM large_table",
  "timeout_ms": 10000
}
```

Default timeout: 30000ms (30 seconds)

**Timeout Error** (408 Request Timeout):
```json
{
  "error": "QueryTimeout",
  "message": "Query exceeded timeout of 10000ms"
}
```

---

## 7. Time-Travel via API

HeliosDB Nano's time-travel feature allows querying historical data using timestamps.

### Time-Travel in Query Endpoint

**Request Body**:
```json
{
  "sql": "SELECT * FROM users WHERE id = $1",
  "params": [{"type": "int4", "value": 1}],
  "as_of": {
    "type": "timestamp",
    "value": 1701446400
  }
}
```

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT * FROM users WHERE id = $1",
    "params": [{"type": "int4", "value": 1}],
    "as_of": {"type": "timestamp", "value": 1701446400}
  }' \
  http://localhost:8080/v1/branches/main/query
```

### Time-Travel in Data Query

Use the `as_of` query parameter:

```bash
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8080/v1/branches/main/tables/users/data?as_of=1701446400"
```

### SQL AS OF Syntax

You can also use SQL's AS OF clause:

```json
{
  "sql": "SELECT * FROM users AS OF SYSTEM TIME 1701446400 WHERE age > 25"
}
```

**Request**:
```bash
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT * FROM users AS OF SYSTEM TIME 1701446400"
  }' \
  http://localhost:8080/v1/branches/main/query
```

### Historical Data Access

**Example: Compare data at two points in time**

```bash
# Current data
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT COUNT(*) as total FROM users"}' \
  http://localhost:8080/v1/branches/main/query

# Historical data (1 hour ago)
TIMESTAMP=$(date -d '1 hour ago' +%s)
curl -X POST \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d "{\"sql\": \"SELECT COUNT(*) as total FROM users\", \"as_of\": {\"type\": \"timestamp\", \"value\": $TIMESTAMP}}" \
  http://localhost:8080/v1/branches/main/query
```

### Use Cases for Time-Travel

1. **Audit and Compliance**: Review data changes over time
2. **Debugging**: Investigate issues by viewing past states
3. **Recovery**: Retrieve accidentally deleted or modified data
4. **Reporting**: Generate reports for specific time periods
5. **Comparison**: Analyze data evolution and trends

---

## 8. Error Handling

### Error Response Format

All errors follow a consistent JSON structure:

```json
{
  "error": "ErrorType",
  "message": "Human-readable error description",
  "details": "Optional additional information"
}
```

### HTTP Response Codes

| Result | Error Type | Description |
|--------|-----------|-------------|
| 400 | BadRequest | Invalid request syntax or parameters |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Insufficient permissions |
| 404 | NotFound | Resource does not exist |
| 408 | QueryTimeout | Query exceeded timeout limit |
| 409 | Conflict | Resource conflict (e.g., branch exists, merge conflict) |
| 422 | UnprocessableEntity | Valid syntax but semantic error |
| 429 | TooManyRequests | Rate limit exceeded |
| 500 | InternalServerError | Server error |
| 501 | NotImplemented | Feature not enabled |

### Common Error Examples

#### 400 Bad Request
```json
{
  "error": "BadRequest",
  "message": "Invalid SQL syntax: unexpected token 'FROM'"
}
```

#### 401 Unauthorized
```json
{
  "error": "Unauthorized",
  "message": "Missing or invalid authentication credentials"
}
```

#### 404 Not Found
```json
{
  "error": "NotFound",
  "message": "Branch 'feature-xyz' not found"
}
```

#### 408 Request Timeout
```json
{
  "error": "QueryTimeout",
  "message": "Query exceeded timeout of 5000ms"
}
```

#### 409 Conflict
```json
{
  "error": "Conflict",
  "message": "Branch 'development' already exists"
}
```

#### 429 Too Many Requests
```json
{
  "error": "TooManyRequests",
  "message": "Rate limit exceeded"
}
```

### Retry Strategies

#### Exponential Backoff

```python
import time
import requests

def retry_with_backoff(url, max_retries=5):
    for attempt in range(max_retries):
        try:
            response = requests.get(url)

            if response.status_code == 429:
                # Rate limited - use Retry-After header
                retry_after = int(response.headers.get('Retry-After', 1))
                time.sleep(retry_after)
                continue

            if response.status_code >= 500:
                # Server error - exponential backoff
                wait_time = min(2 ** attempt, 60)
                time.sleep(wait_time)
                continue

            return response

        except requests.RequestException as e:
            if attempt == max_retries - 1:
                raise
            time.sleep(2 ** attempt)

    raise Exception("Max retries exceeded")
```

#### Rate Limit Handling

When you receive a 429 response:

```bash
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1701446460
Retry-After: 60
```

Wait for the time specified in `Retry-After` header before retrying.

---

## 9. SDK Examples

### curl Examples

#### List Branches
```bash
curl -H "Authorization: Bearer ${TOKEN}" \
  http://localhost:8080/v1/branches
```

#### Create Branch
```bash
curl -X POST \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{"name": "dev", "parent": "main"}' \
  http://localhost:8080/v1/branches
```

#### Execute Query
```bash
curl -X POST \
  -H "Authorization: Bearer ${TOKEN}" \
  -H "Content-Type: application/json" \
  -d '{
    "sql": "SELECT * FROM users LIMIT 10"
  }' \
  http://localhost:8080/v1/branches/main/query
```

### Python requests Examples

```python
import requests
import json

class HeliosDBClient:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }

    def list_branches(self):
        """List all branches"""
        response = requests.get(
            f"{self.base_url}/v1/branches",
            headers=self.headers
        )
        response.raise_for_status()
        return response.json()

    def create_branch(self, name, parent='main'):
        """Create a new branch"""
        response = requests.post(
            f"{self.base_url}/v1/branches",
            headers=self.headers,
            json={'name': name, 'parent': parent}
        )
        response.raise_for_status()
        return response.json()

    def execute_query(self, branch, sql, params=None):
        """Execute a SQL query"""
        payload = {'sql': sql}
        if params:
            payload['params'] = params

        response = requests.post(
            f"{self.base_url}/v1/branches/{branch}/query",
            headers=self.headers,
            json=payload
        )
        response.raise_for_status()
        return response.json()

    def insert_data(self, branch, table, rows):
        """Insert data into a table"""
        response = requests.post(
            f"{self.base_url}/v1/branches/{branch}/tables/{table}/data",
            headers=self.headers,
            json={'rows': rows}
        )
        response.raise_for_status()
        return response.json()

# Usage
client = HeliosDBClient('http://localhost:8080', 'sk_test_abc123')

# List branches
branches = client.list_branches()
print(f"Found {branches['total']} branches")

# Create branch
new_branch = client.create_branch('feature-xyz')
print(f"Created branch: {new_branch['name']}")

# Execute query
results = client.execute_query(
    'main',
    'SELECT * FROM users WHERE age > $1',
    params=[{'type': 'int4', 'value': 25}]
)
print(f"Query returned {results['row_count']} rows")

# Insert data
insert_result = client.insert_data(
    'main',
    'users',
    [{'id': 500, 'name': 'Alice', 'email': 'alice@example.com'}]
)
print(f"Inserted {insert_result['inserted']} rows")
```

### JavaScript fetch Examples

```javascript
class HeliosDBClient {
    constructor(baseUrl, apiKey) {
        this.baseUrl = baseUrl;
        this.headers = {
            'X-API-Key': apiKey,
            'Content-Type': 'application/json'
        };
    }

    async listBranches() {
        const response = await fetch(`${this.baseUrl}/v1/branches`, {
            headers: this.headers
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        return await response.json();
    }

    async createBranch(name, parent = 'main') {
        const response = await fetch(`${this.baseUrl}/v1/branches`, {
            method: 'POST',
            headers: this.headers,
            body: JSON.stringify({ name, parent })
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        return await response.json();
    }

    async executeQuery(branch, sql, params = null) {
        const payload = { sql };
        if (params) {
            payload.params = params;
        }

        const response = await fetch(
            `${this.baseUrl}/v1/branches/${branch}/query`,
            {
                method: 'POST',
                headers: this.headers,
                body: JSON.stringify(payload)
            }
        );

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        return await response.json();
    }

    async insertData(branch, table, rows) {
        const response = await fetch(
            `${this.baseUrl}/v1/branches/${branch}/tables/${table}/data`,
            {
                method: 'POST',
                headers: this.headers,
                body: JSON.stringify({ rows })
            }
        );

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        return await response.json();
    }
}

// Usage
const client = new HeliosDBClient('http://localhost:8080', 'sk_test_abc123');

// List branches
const branches = await client.listBranches();
console.log(`Found ${branches.total} branches`);

// Create branch
const newBranch = await client.createBranch('feature-xyz');
console.log(`Created branch: ${newBranch.name}`);

// Execute query
const results = await client.executeQuery(
    'main',
    'SELECT * FROM users WHERE age > $1',
    [{ type: 'int4', value: 25 }]
);
console.log(`Query returned ${results.row_count} rows`);

// Insert data
const insertResult = await client.insertData(
    'main',
    'users',
    [{ id: 500, name: 'Alice', email: 'alice@example.com' }]
);
console.log(`Inserted ${insertResult.inserted} rows`);
```

### Go net/http Example

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

type HeliosDBClient struct {
    BaseURL string
    APIKey  string
    Client  *http.Client
}

func NewClient(baseURL, apiKey string) *HeliosDBClient {
    return &HeliosDBClient{
        BaseURL: baseURL,
        APIKey:  apiKey,
        Client:  &http.Client{},
    }
}

func (c *HeliosDBClient) request(method, path string, body interface{}) (*http.Response, error) {
    var bodyReader io.Reader
    if body != nil {
        jsonBody, err := json.Marshal(body)
        if err != nil {
            return nil, err
        }
        bodyReader = bytes.NewBuffer(jsonBody)
    }

    req, err := http.NewRequest(method, c.BaseURL+path, bodyReader)
    if err != nil {
        return nil, err
    }

    req.Header.Set("X-API-Key", c.APIKey)
    req.Header.Set("Content-Type", "application/json")

    return c.Client.Do(req)
}

func (c *HeliosDBClient) ListBranches() (map[string]interface{}, error) {
    resp, err := c.request("GET", "/v1/branches", nil)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, err
    }

    return result, nil
}

func (c *HeliosDBClient) ExecuteQuery(branch, sql string) (map[string]interface{}, error) {
    payload := map[string]interface{}{
        "sql": sql,
    }

    resp, err := c.request("POST", "/v1/branches/"+branch+"/query", payload)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    var result map[string]interface{}
    if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
        return nil, err
    }

    return result, nil
}

func main() {
    client := NewClient("http://localhost:8080", "sk_test_abc123")

    // List branches
    branches, err := client.ListBranches()
    if err != nil {
        panic(err)
    }
    fmt.Printf("Found %v branches\n", branches["total"])

    // Execute query
    results, err := client.ExecuteQuery("main", "SELECT * FROM users LIMIT 10")
    if err != nil {
        panic(err)
    }
    fmt.Printf("Query returned %v rows\n", results["row_count"])
}
```

---

## 10. Rate Limiting

### Understanding Rate Limits

HeliosDB Nano uses a token bucket algorithm for rate limiting:

- **Limit**: Maximum requests allowed in the time window
- **Remaining**: Requests available in current window
- **Reset**: Unix timestamp when the limit resets
- **Retry-After**: Seconds to wait before retrying (when limited)

### Rate Limit Headers

All responses include rate limit information:

```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1701446460
```

### Limits by Endpoint

Default limits (configurable):

| Endpoint Type | Requests/Minute | Burst |
|---------------|-----------------|-------|
| Public | 30 | 5 |
| Authenticated | 100 | 10 |
| Heavy Operations | 10 | 2 |

Heavy operations include:
- Large data queries
- Merge operations
- Complex SQL queries

### Handling 429 Responses

When rate limited, you receive:

```bash
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1701446460
Retry-After: 30

{
  "error": "TooManyRequests",
  "message": "Rate limit exceeded"
}
```

**Best Practices**:

1. **Check rate limit headers** before hitting the limit
2. **Use Retry-After header** for wait time
3. **Implement exponential backoff** for retries
4. **Cache responses** when appropriate
5. **Batch operations** to reduce request count

### Rate Limit Example Implementation

```python
import requests
import time

class RateLimitedClient:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.api_key = api_key
        self.remaining = None
        self.reset_at = None

    def _update_rate_limits(self, response):
        """Update rate limit info from response headers"""
        self.remaining = int(response.headers.get('X-RateLimit-Remaining', 0))
        self.reset_at = int(response.headers.get('X-RateLimit-Reset', 0))

    def _should_wait(self):
        """Check if we should wait before making request"""
        if self.remaining is not None and self.remaining < 5:
            if self.reset_at:
                wait_time = max(0, self.reset_at - time.time())
                if wait_time > 0:
                    print(f"Approaching rate limit. Waiting {wait_time:.1f}s")
                    time.sleep(wait_time)
                    return True
        return False

    def request(self, method, path, **kwargs):
        """Make rate-limited request"""
        self._should_wait()

        headers = kwargs.get('headers', {})
        headers['X-API-Key'] = self.api_key
        kwargs['headers'] = headers

        response = requests.request(method, self.base_url + path, **kwargs)
        self._update_rate_limits(response)

        if response.status_code == 429:
            retry_after = int(response.headers.get('Retry-After', 60))
            print(f"Rate limited. Retrying after {retry_after}s")
            time.sleep(retry_after)
            return self.request(method, path, **kwargs)

        response.raise_for_status()
        return response

# Usage
client = RateLimitedClient('http://localhost:8080', 'sk_test_abc123')

# Make many requests safely
for i in range(200):
    response = client.request('GET', '/v1/branches')
    print(f"Request {i+1}: {client.remaining} remaining")
```

---

## Appendix

### Complete Working Example

This example demonstrates a complete workflow using the REST API:

```python
import requests
import time

class HeliosDBWorkflow:
    def __init__(self, base_url, api_key):
        self.base_url = base_url
        self.headers = {
            'X-API-Key': api_key,
            'Content-Type': 'application/json'
        }

    def run(self):
        print("=== HeliosDB Nano REST API Workflow ===\n")

        # 1. Create a feature branch
        print("1. Creating feature branch...")
        branch = self._create_branch('feature-demo')
        print(f"   Created: {branch['name']}\n")

        # 2. Create a table
        print("2. Creating products table...")
        self._execute_statement(
            'feature-demo',
            '''CREATE TABLE products (
                id INTEGER PRIMARY KEY,
                name TEXT NOT NULL,
                price REAL,
                created_at INTEGER
            )'''
        )
        print("   Table created\n")

        # 3. Insert data
        print("3. Inserting sample data...")
        result = self._insert_data('feature-demo', 'products', [
            {'id': 1, 'name': 'Widget', 'price': 9.99, 'created_at': int(time.time())},
            {'id': 2, 'name': 'Gadget', 'price': 19.99, 'created_at': int(time.time())},
            {'id': 3, 'name': 'Doohickey', 'price': 14.99, 'created_at': int(time.time())},
        ])
        print(f"   Inserted {result['inserted']} rows\n")

        # 4. Query data
        print("4. Querying data...")
        results = self._execute_query(
            'feature-demo',
            'SELECT * FROM products WHERE price > $1 ORDER BY price DESC',
            [{'type': 'float8', 'value': 10.0}]
        )
        print(f"   Found {results['row_count']} products over $10:")
        for row in results['rows']:
            print(f"   - {row['name']}: ${row['price']}")
        print()

        # 5. Merge to main
        print("5. Merging feature-demo into main...")
        merge_result = self._merge_branch('feature-demo', 'main')
        print(f"   {merge_result['message']}\n")

        # 6. Verify in main branch
        print("6. Verifying data in main branch...")
        results = self._execute_query('main', 'SELECT COUNT(*) as total FROM products')
        print(f"   Total products in main: {results['rows'][0]['total']}\n")

        # 7. Cleanup
        print("7. Cleaning up feature branch...")
        self._delete_branch('feature-demo')
        print("   Branch deleted\n")

        print("=== Workflow Complete ===")

    def _create_branch(self, name):
        response = requests.post(
            f"{self.base_url}/v1/branches",
            headers=self.headers,
            json={'name': name, 'parent': 'main'}
        )
        response.raise_for_status()
        return response.json()

    def _execute_statement(self, branch, sql):
        response = requests.post(
            f"{self.base_url}/v1/branches/{branch}/execute",
            headers=self.headers,
            json={'sql': sql}
        )
        response.raise_for_status()
        return response.json()

    def _insert_data(self, branch, table, rows):
        response = requests.post(
            f"{self.base_url}/v1/branches/{branch}/tables/{table}/data",
            headers=self.headers,
            json={'rows': rows}
        )
        response.raise_for_status()
        return response.json()

    def _execute_query(self, branch, sql, params=None):
        payload = {'sql': sql}
        if params:
            payload['params'] = params

        response = requests.post(
            f"{self.base_url}/v1/branches/{branch}/query",
            headers=self.headers,
            json=payload
        )
        response.raise_for_status()
        return response.json()

    def _merge_branch(self, source, target):
        response = requests.post(
            f"{self.base_url}/v1/branches/{source}/merge",
            headers=self.headers,
            json={'target': target, 'strategy': 'Auto'}
        )
        response.raise_for_status()
        return response.json()

    def _delete_branch(self, name):
        response = requests.delete(
            f"{self.base_url}/v1/branches/{name}",
            headers=self.headers
        )
        response.raise_for_status()

# Run the workflow
if __name__ == '__main__':
    workflow = HeliosDBWorkflow('http://localhost:8080', 'sk_test_abc123')
    workflow.run()
```

### Quick Reference

**Health & Version**
```bash
GET  /health              # Health check
GET  /version             # Version info
```

**Branches**
```bash
GET    /v1/branches              # List branches
POST   /v1/branches              # Create branch
GET    /v1/branches/{name}       # Get branch
DELETE /v1/branches/{name}       # Delete branch
POST   /v1/branches/{name}/merge # Merge branch
```

**SQL Execution**
```bash
POST /v1/branches/{name}/query   # Execute query (SELECT)
POST /v1/branches/{name}/execute # Execute statement (DDL/DML)
```

**Data Operations**
```bash
GET    /v1/branches/{name}/tables                   # List tables
GET    /v1/branches/{name}/tables/{table}/data      # Query data
POST   /v1/branches/{name}/tables/{table}/data      # Insert data
PUT    /v1/branches/{name}/tables/{table}/data      # Update data
DELETE /v1/branches/{name}/tables/{table}/data      # Delete data
```

---

## Conclusion

This tutorial covered the complete HeliosDB Nano REST API, including:

- Server setup and configuration
- Authentication with JWT and API keys
- Branch management operations
- Data CRUD operations
- SQL query execution
- Time-travel queries
- Error handling and retry strategies
- Rate limiting
- SDK examples in multiple languages

For more information:
- API Specification: `/docs/api/BRANCH_REST_API_SPECIFICATION.md`
- HTTP Sync API: `/docs/guides/api/HTTP_SYNC_API.md`
- Examples: `/examples/` directory

**Next Steps**:
1. Set up a development server
2. Generate authentication credentials
3. Try the example workflows
4. Integrate with your application
5. Implement proper error handling and rate limiting

