HeliosDB Nano Branch REST API Specification
HeliosDB Nano Branch REST API Specification
Version: 1.0.0 Status: Draft Created: 2025-12-01
Overview
This document specifies the RESTful API for managing database branches in HeliosDB Nano. The API enables creating, managing, and working with data across branches via HTTP.
Base URL
Production: https://api.heliosdb.local/v1Development: http://localhost:8080/v1Authentication
All endpoints require authentication via one of:
Bearer Token (JWT)
Authorization: Bearer <jwt_token>API Key
X-API-Key: <api_key>Common Response Formats
Success Response
{ "success": true, "data": { ... }, "meta": { "request_id": "req_abc123", "execution_time_ms": 45 }}Error Response
{ "success": false, "error": { "code": "BRANCH_NOT_FOUND", "message": "Branch 'feature-xyz' does not exist", "details": { ... } }, "meta": { "request_id": "req_abc123" }}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
BRANCH_NOT_FOUND | 404 | Branch does not exist |
BRANCH_EXISTS | 409 | Branch name already in use |
BRANCH_PROTECTED | 403 | Cannot modify protected branch |
MERGE_CONFLICT | 409 | Unresolved merge conflicts |
INVALID_SQL | 400 | SQL syntax error |
UNAUTHORIZED | 401 | Invalid or missing auth |
FORBIDDEN | 403 | Insufficient permissions |
RATE_LIMITED | 429 | Too many requests |
INTERNAL_ERROR | 500 | Server error |
Endpoints
Branch Management
List All Branches
GET /v1/branchesQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | all | Filter: active, merged, deleted, all |
page | integer | 1 | Page number |
limit | integer | 20 | Results per page (max 100) |
sort | string | created_at | Sort field: name, created_at, updated_at |
order | string | desc | Sort order: asc, desc |
Response:
{ "success": true, "data": { "branches": [ { "id": "br_main_001", "name": "main", "display_name": "Main Branch", "status": "active", "is_default": true, "is_protected": true, "created_at": "2025-01-01T00:00:00Z", "updated_at": "2025-12-01T10:00:00Z", "stats": { "tables": 15, "rows": 125000, "size_bytes": 52428800 } }, { "id": "br_feat_abc123", "name": "feature-auth", "display_name": "Authentication Feature", "status": "active", "is_default": false, "is_protected": false, "source_branch": "main", "source_snapshot": "snap_xyz789", "created_at": "2025-12-01T08:00:00Z", "updated_at": "2025-12-01T09:30:00Z", "stats": { "tables": 15, "rows": 125050, "size_bytes": 52430000, "changes_from_source": 50 } } ], "pagination": { "page": 1, "limit": 20, "total": 2, "total_pages": 1 } }}Create Branch
POST /v1/branchesRequest Body:
{ "name": "feature-auth", "display_name": "Authentication Feature", "source_branch": "main", "as_of": { "type": "timestamp", "value": "2025-12-01T08:00:00Z" }, "metadata": { "owner": "alice@example.com", "ticket": "JIRA-1234", "description": "Implementing OAuth2 authentication" }}As-Of Options:
// Current state{ "type": "now" }
// Specific timestamp{ "type": "timestamp", "value": "2025-12-01T08:00:00Z" }
// After specific transaction{ "type": "transaction", "value": 12345 }
// Specific SCN (System Change Number){ "type": "scn", "value": 987654 }
// Specific snapshot{ "type": "snapshot", "value": "snap_xyz789" }Response:
{ "success": true, "data": { "id": "br_feat_abc123", "name": "feature-auth", "display_name": "Authentication Feature", "status": "active", "source_branch": "main", "source_snapshot": "snap_xyz789", "created_at": "2025-12-01T10:00:00Z" }}Get Branch Details
GET /v1/branches/{branch_name}Path Parameters:
| Parameter | Type | Description |
|---|---|---|
branch_name | string | Branch name or ID |
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
include_stats | boolean | true | Include statistics |
include_history | boolean | false | Include recent history |
Response:
{ "success": true, "data": { "id": "br_feat_abc123", "name": "feature-auth", "display_name": "Authentication Feature", "status": "active", "is_default": false, "is_protected": false, "source_branch": "main", "source_snapshot": "snap_xyz789", "created_at": "2025-12-01T08:00:00Z", "updated_at": "2025-12-01T09:30:00Z", "metadata": { "owner": "alice@example.com", "ticket": "JIRA-1234", "description": "Implementing OAuth2 authentication" }, "stats": { "tables": 15, "rows": 125050, "size_bytes": 52430000, "changes_from_source": 50, "inserts": 30, "updates": 15, "deletes": 5 }, "divergence": { "source_branch": "main", "commits_ahead": 5, "commits_behind": 2, "can_fast_forward": false } }}Update Branch
PATCH /v1/branches/{branch_name}Request Body:
{ "display_name": "Updated Display Name", "is_protected": true, "metadata": { "owner": "bob@example.com" }}Response:
{ "success": true, "data": { "id": "br_feat_abc123", "name": "feature-auth", "display_name": "Updated Display Name", "is_protected": true, "updated_at": "2025-12-01T11:00:00Z" }}Delete Branch
DELETE /v1/branches/{branch_name}Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
force | boolean | false | Force delete even with uncommitted changes |
Response:
{ "success": true, "data": { "deleted": true, "branch_name": "feature-auth", "deleted_at": "2025-12-01T12:00:00Z" }}Merge Branches
POST /v1/branches/{branch_name}/mergeRequest Body:
{ "target_branch": "main", "conflict_resolution": "source_wins", "delete_source_after_merge": true, "merge_message": "Merge feature-auth into main", "dry_run": false}Conflict Resolution Options:
source_wins- Source branch values take precedencetarget_wins- Target branch values take precedencemanual- Return conflicts for manual resolutionabort- Fail if any conflicts exist
Response (Success):
{ "success": true, "data": { "status": "merged", "source_branch": "feature-auth", "target_branch": "main", "merge_commit": "mc_def456", "changes_merged": 50, "conflicts_resolved": 3, "source_deleted": true, "merged_at": "2025-12-01T13:00:00Z" }}Response (Conflicts):
{ "success": false, "error": { "code": "MERGE_CONFLICT", "message": "Merge has conflicts requiring resolution", "details": { "conflicts": [ { "table": "users", "row_id": "123", "column": "email", "source_value": "alice@new.com", "target_value": "alice@old.com", "conflict_type": "update_update" } ] } }}Data Operations
List Tables in Branch
GET /v1/branches/{branch_name}/tablesResponse:
{ "success": true, "data": { "tables": [ { "name": "users", "schema": "public", "row_count": 10000, "columns": [ {"name": "id", "type": "INTEGER", "nullable": false, "primary_key": true}, {"name": "name", "type": "TEXT", "nullable": false}, {"name": "email", "type": "TEXT", "nullable": false}, {"name": "created_at", "type": "TIMESTAMP", "nullable": false} ], "indexes": ["idx_users_email"], "created_at": "2025-01-01T00:00:00Z" } ] }}Query Table Data
GET /v1/branches/{branch_name}/tables/{table_name}/dataQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
columns | string | * | Comma-separated column names |
filter | string | - | WHERE clause (URL encoded) |
order_by | string | - | ORDER BY clause |
page | integer | 1 | Page number |
limit | integer | 100 | Rows per page (max 1000) |
as_of_type | string | now | Time-travel type |
as_of_value | string | - | Time-travel value |
Example:
GET /v1/branches/main/tables/users/data?columns=id,name,email&filter=active%3Dtrue&limit=50Response:
{ "success": true, "data": { "columns": ["id", "name", "email"], "rows": [ [1, "Alice", "alice@example.com"], [2, "Bob", "bob@example.com"] ], "pagination": { "page": 1, "limit": 50, "total": 2, "total_pages": 1 } }}Insert Data
POST /v1/branches/{branch_name}/tables/{table_name}/dataRequest Body:
{ "rows": [ {"name": "Charlie", "email": "charlie@example.com"}, {"name": "Diana", "email": "diana@example.com"} ], "on_conflict": "error"}On Conflict Options:
error- Return error on conflictignore- Skip conflicting rowsupdate- Update existing rows
Response:
{ "success": true, "data": { "inserted": 2, "ids": [3, 4] }}Update Data
PUT /v1/branches/{branch_name}/tables/{table_name}/dataRequest Body:
{ "filter": "id = 1", "updates": { "email": "alice@newdomain.com", "updated_at": "2025-12-01T14:00:00Z" }}Response:
{ "success": true, "data": { "updated": 1 }}Delete Data
DELETE /v1/branches/{branch_name}/tables/{table_name}/dataQuery Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
filter | string | Yes | WHERE clause (required for safety) |
Response:
{ "success": true, "data": { "deleted": 5 }}SQL Operations
Execute Query (Read-Only)
POST /v1/branches/{branch_name}/queryRequest Body:
{ "sql": "SELECT u.name, COUNT(o.id) as order_count FROM users u LEFT JOIN orders o ON u.id = o.user_id GROUP BY u.name ORDER BY order_count DESC LIMIT 10", "params": [], "as_of": { "type": "timestamp", "value": "2025-12-01T08:00:00Z" }, "timeout_ms": 30000}Response:
{ "success": true, "data": { "columns": ["name", "order_count"], "column_types": ["TEXT", "BIGINT"], "rows": [ ["Alice", 150], ["Bob", 120], ["Charlie", 95] ], "row_count": 3, "execution_plan": "...", "execution_time_ms": 45 }}Execute Statement (DDL/DML)
POST /v1/branches/{branch_name}/executeRequest Body:
{ "sql": "CREATE TABLE products (id INTEGER PRIMARY KEY, name TEXT NOT NULL, price DECIMAL(10,2))", "params": [], "timeout_ms": 30000}Response:
{ "success": true, "data": { "statement_type": "CREATE_TABLE", "affected_rows": 0, "execution_time_ms": 12 }}Time-Travel Operations
Get Branch History
GET /v1/branches/{branch_name}/historyQuery Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
from | string | - | Start timestamp |
to | string | - | End timestamp |
limit | integer | 50 | Max results |
Response:
{ "success": true, "data": { "history": [ { "id": "tx_001", "type": "transaction", "timestamp": "2025-12-01T10:00:00Z", "scn": 1000, "summary": "INSERT INTO users (5 rows)", "user": "alice@example.com" }, { "id": "snap_001", "type": "snapshot", "timestamp": "2025-12-01T09:00:00Z", "scn": 900, "summary": "Automatic snapshot", "size_bytes": 52428800 } ] }}List Snapshots
GET /v1/branches/{branch_name}/snapshotsResponse:
{ "success": true, "data": { "snapshots": [ { "id": "snap_xyz789", "timestamp": "2025-12-01T09:00:00Z", "scn": 900, "type": "automatic", "size_bytes": 52428800, "tables": 15, "rows": 125000 } ] }}Restore to Snapshot
POST /v1/branches/{branch_name}/restore/{snapshot_id}Request Body:
{ "create_backup_branch": true, "backup_branch_name": "backup-before-restore"}Response:
{ "success": true, "data": { "restored": true, "snapshot_id": "snap_xyz789", "restored_to_scn": 900, "backup_branch": "backup-before-restore" }}Rate Limiting
| Endpoint Category | Rate Limit |
|---|---|
| Read operations | 1000/minute |
| Write operations | 100/minute |
| Query execution | 60/minute |
| Branch creation | 10/minute |
Rate limit headers:
X-RateLimit-Limit: 1000X-RateLimit-Remaining: 950X-RateLimit-Reset: 1701432060Webhooks (Future)
Branch events can trigger webhooks:
{ "event": "branch.created", "timestamp": "2025-12-01T10:00:00Z", "data": { "branch_id": "br_feat_abc123", "branch_name": "feature-auth", "source_branch": "main" }}Events:
branch.createdbranch.deletedbranch.mergedbranch.protectedbranch.data_changed
SDK Examples
Rust
use heliosdb_nano_client::{Client, BranchCreateOptions};
let client = Client::new("http://localhost:8080") .with_api_key("your-api-key");
// Create branchlet branch = client.branches() .create("feature-auth") .from_branch("main") .as_of_now() .send() .await?;
// Query datalet results = client.branches() .get("feature-auth") .query("SELECT * FROM users WHERE active = true") .send() .await?;
// Merge branchclient.branches() .get("feature-auth") .merge_into("main") .with_conflict_resolution(ConflictResolution::SourceWins) .delete_after_merge(true) .send() .await?;JavaScript/TypeScript
import { HeliosDBClient } from '@heliosdb/client';
const client = new HeliosDBClient({ baseUrl: 'http://localhost:8080', apiKey: 'your-api-key'});
// Create branchconst branch = await client.branches.create({ name: 'feature-auth', sourceBranch: 'main', asOf: { type: 'now' }});
// Query dataconst results = await client.branches.query('feature-auth', { sql: 'SELECT * FROM users WHERE active = true'});
// Merge branchawait client.branches.merge('feature-auth', { targetBranch: 'main', conflictResolution: 'source_wins', deleteAfterMerge: true});Python
from heliosdb import HeliosDBClient
client = HeliosDBClient( base_url="http://localhost:8080", api_key="your-api-key")
# Create branchbranch = client.branches.create( name="feature-auth", source_branch="main", as_of={"type": "now"})
# Query dataresults = client.branches.query( "feature-auth", sql="SELECT * FROM users WHERE active = true")
# Merge branchclient.branches.merge( "feature-auth", target_branch="main", conflict_resolution="source_wins", delete_after_merge=True)OpenAPI Specification
Full OpenAPI 3.0 specification available at:
/v1/openapi.json/v1/openapi.yaml
Interactive documentation at:
/v1/docs(Swagger UI)/v1/redoc(ReDoc)
Document Version: 1.0.0 Last Updated: 2025-12-01