Skip to content

API Reference

Faucet exposes a REST API at /api/v1 with two main sections:

  • System API (/api/v1/system/) -- manage Faucet configuration (services, roles, API keys, admins)
  • Service API (/api/v1/{serviceName}/) -- query and modify data in connected databases

Authentication

All API endpoints (except login, health checks, and OpenAPI spec) require authentication. Faucet supports two authentication methods:

API Key (X-API-Key header)

API keys are bound to roles that define what operations are allowed. Use the X-API-Key header:

bash
curl http://localhost:8080/api/v1/mydb/_table/users \
  -H "X-API-Key: faucet_a1b2c3d4e5f6..."

JWT Bearer Token (Authorization header)

Admin users authenticate with email/password and receive a JWT token. Use the Authorization: Bearer header:

bash
curl http://localhost:8080/api/v1/mydb/_table/users \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Authentication Flow

Request arrives
  |
  +-- X-API-Key header present?
  |     YES -> Validate key hash against stored keys -> Principal{type:"api_key", role_id:N}
  |     NO  -> Continue
  |
  +-- Authorization: Bearer <token> present?
  |     YES -> Validate JWT signature + claims -> Principal{type:"admin", admin_id:N}
  |     NO  -> 401 Unauthorized

Obtaining a JWT Token

bash
curl -X POST http://localhost:8080/api/v1/system/admin/session \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@example.com", "password": "yourpassword"}'

Response:

json
{
  "session_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 86400,
  "admin_id": 1,
  "email": "admin@example.com",
  "name": "Admin"
}

Tokens expire after 24 hours by default.


Response Format

Success (list)

All list endpoints return a resource array with optional meta for pagination:

json
{
  "resource": [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"}
  ],
  "meta": {
    "count": 2,
    "total": 150,
    "limit": 25,
    "offset": 0,
    "took_ms": 3.45
  }
}

Meta fields:

FieldTypeDescription
countintegerNumber of records in this response
totalintegerTotal matching records (only when include_count=true)
limitintegerMaximum records per page
offsetintegerNumber of records skipped
took_msfloatQuery execution time in milliseconds

Success (single)

Single-resource endpoints return the resource object directly (not wrapped in resource).

Error

json
{
  "error": {
    "code": 404,
    "message": "Table not found: nonexistent",
    "context": {
      "service": "mydb",
      "table": "nonexistent"
    }
  }
}
FieldTypeDescription
codeintegerHTTP status code
messagestringHuman-readable error description
contextobjectAdditional error details (optional)

Health Check Endpoints

These endpoints require no authentication.

GET /healthz

Liveness probe. Returns 200 if the process is running.

json
{"status": "ok"}

GET /readyz

Readiness probe. Returns 200 when the server is ready to accept traffic. Pings all database services and reports their status. Returns 503 if any service is unhealthy.

json
{
  "status": "ok",
  "checks": {
    "mydb": "ok",
    "analytics": "ok"
  }
}

GET /openapi.json

Returns the combined OpenAPI 3.1 specification for all connected services.


System API

All system endpoints (except session management) require admin authentication.

Session Management

POST /api/v1/system/admin/session

Authenticate and obtain a JWT token.

Request body:

json
{
  "email": "admin@example.com",
  "password": "yourpassword"
}

Response (200):

json
{
  "session_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 86400,
  "admin_id": 1,
  "email": "admin@example.com",
  "name": "Admin"
}

DELETE /api/v1/system/admin/session

Log out (instructs client to discard token).

Response (200):

json
{
  "success": true,
  "message": "Session invalidated"
}

Service Management

GET /api/v1/system/service

List all configured database services.

Response (200):

json
{
  "resource": [
    {
      "id": 1,
      "name": "mydb",
      "label": "My Database",
      "driver": "postgres",
      "schema": "public",
      "read_only": false,
      "raw_sql_allowed": false,
      "is_active": true,
      "created_at": "2025-01-15T10:30:00Z",
      "updated_at": "2025-01-15T10:30:00Z"
    }
  ],
  "meta": {"count": 1}
}

Note: The DSN is never exposed in list responses.

POST /api/v1/system/service

Create a new database service.

Request body:

json
{
  "name": "mydb",
  "label": "My Database",
  "driver": "postgres",
  "dsn": "postgres://user:pass@localhost:5432/mydb?sslmode=disable",
  "schema": "public",
  "read_only": false,
  "raw_sql_allowed": false
}

Required fields: name, driver, dsn

GET /api/v1/system/service/

Get a single service by name.

PUT /api/v1/system/service/

Update a service configuration. Only non-empty fields in the request body are applied.

DELETE /api/v1/system/service/

Delete a service and disconnect it.

Role Management

GET /api/v1/system/role

List all roles.

POST /api/v1/system/role

Create a new role with optional access rules.

Request body:

json
{
  "name": "readonly",
  "description": "Read-only access to all services",
  "access": [
    {
      "service_name": "mydb",
      "component": "_table/*",
      "verb_mask": 1
    }
  ]
}

See RBAC for verb mask values and access rule details.

GET /api/v1/system/role/

Get a single role by ID, including its access rules.

PUT /api/v1/system/role/

Update a role. If access is provided, it replaces all existing access rules.

DELETE /api/v1/system/role/

Delete a role by ID.

Admin Management

GET /api/v1/system/admin

List all admin accounts (passwords are never exposed).

POST /api/v1/system/admin

Create a new admin account.

Request body:

json
{
  "email": "newadmin@example.com",
  "password": "securepassword",
  "name": "New Admin"
}

Password must be at least 8 characters.

API Key Management

GET /api/v1/system/api-key

List all API keys. The actual key value is never shown -- only the prefix.

Response (200):

json
{
  "resource": [
    {
      "id": 1,
      "key_prefix": "faucet_a1b2c3d",
      "label": "CI pipeline",
      "role_id": 2,
      "is_active": true,
      "created_at": "2025-01-15T10:30:00Z",
      "last_used": "2025-01-16T08:00:00Z"
    }
  ],
  "meta": {"count": 1}
}

POST /api/v1/system/api-key

Create a new API key. The plaintext key is returned once in the response and cannot be retrieved again.

Request body:

json
{
  "role_id": 2,
  "label": "CI pipeline",
  "expires_at": "2026-01-01T00:00:00Z"
}

Required field: role_id

Response (201):

json
{
  "id": 1,
  "api_key": "faucet_a1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef12345678",
  "key_prefix": "faucet_a1b2c3d",
  "label": "CI pipeline",
  "role_id": 2,
  "is_active": true,
  "created_at": "2025-01-15T10:30:00Z"
}

Save the api_key value immediately. It cannot be retrieved after this response.

DELETE /api/v1/system/api-key/

Revoke (deactivate) an API key by ID.

Connection Testing

GET /api/v1/system/service/{serviceName}/test

Test connectivity to a database service by pinging it.

Response (200):

json
{
  "success": true,
  "message": "Connection successful"
}

MCP Configuration

GET /api/v1/system/mcp

Returns MCP server configuration, available tools, resources, and transports.


Table CRUD Endpoints

These endpoints operate on data in connected databases. The URL pattern is:

/api/v1/{serviceName}/_table/{tableName}

Where {serviceName} is the name of a registered database service and {tableName} is a table in that database.

GET /api/v1/{serviceName}/_table

List all table names in the service's database.

Response (200):

json
{
  "resource": [
    {"name": "users"},
    {"name": "orders"},
    {"name": "products"}
  ]
}

GET /api/v1/{serviceName}/_table/

Query records from a table.

Query parameters:

ParameterTypeDefaultDescription
filterstring--Filter expression (syntax)
fieldsstringallComma-separated column names to return
orderstring--Sort order (e.g., name ASC, created_at DESC)
limitinteger25Max records to return (capped at 1000)
offsetinteger0Number of records to skip
include_countbooleanfalseInclude total count in meta
idsstring--Comma-separated primary key values

Examples:

bash
# Basic query
curl "http://localhost:8080/api/v1/mydb/_table/users"

# With filtering
curl "http://localhost:8080/api/v1/mydb/_table/users?filter=age%20%3E%2021%20AND%20status%20%3D%20'active'"

# Select specific fields
curl "http://localhost:8080/api/v1/mydb/_table/users?fields=id,name,email"

# With ordering and pagination
curl "http://localhost:8080/api/v1/mydb/_table/users?order=created_at%20DESC&limit=10&offset=20"

# Include total count
curl "http://localhost:8080/api/v1/mydb/_table/users?include_count=true&limit=10"

Response (200):

json
{
  "resource": [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"}
  ],
  "meta": {
    "count": 2,
    "limit": 25,
    "offset": 0,
    "took_ms": 1.23
  }
}

NDJSON streaming: Set Accept: application/x-ndjson to receive results as newline-delimited JSON (one JSON object per line). Useful for large result sets.

POST /api/v1/{serviceName}/_table/

Insert one or more records. Accepts three body formats:

Single record:

json
{"name": "Alice", "email": "alice@example.com"}

Array of records:

json
[
  {"name": "Alice", "email": "alice@example.com"},
  {"name": "Bob", "email": "bob@example.com"}
]

Resource envelope (DreamFactory-compatible):

json
{
  "resource": [
    {"name": "Alice", "email": "alice@example.com"},
    {"name": "Bob", "email": "bob@example.com"}
  ]
}

Response (201): For databases that support RETURNING (PostgreSQL), the created records including auto-generated fields are returned. For others, the input records plus a row count are returned.

json
{
  "resource": [
    {"id": 1, "name": "Alice", "email": "alice@example.com", "created_at": "2025-01-15T10:30:00Z"}
  ],
  "meta": {
    "count": 1,
    "took_ms": 2.45
  }
}

PUT /api/v1/{serviceName}/_table/

Replace records (full update). Each record in the body must include its primary key (id field) or a filter query parameter must be provided.

Request body:

json
{
  "resource": [
    {"id": 1, "name": "Alice Updated", "email": "alice.new@example.com", "status": "active"}
  ]
}

PATCH /api/v1/{serviceName}/_table/

Partial update of records matching a filter or ID list. Only the provided fields are modified.

Query parameters: filter or ids (at least one required)

Request body:

json
{"status": "archived"}

Or with IDs in the body:

json
{
  "ids": [1, 2, 3],
  "status": "archived"
}

Examples:

bash
# Update by filter
curl -X PATCH "http://localhost:8080/api/v1/mydb/_table/users?filter=status%20%3D%20'inactive'" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "archived"}'

# Update by IDs
curl -X PATCH "http://localhost:8080/api/v1/mydb/_table/users?ids=1,2,3" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status": "archived"}'

DELETE /api/v1/{serviceName}/_table/

Delete records matching a filter or ID list. A filter or IDs must be provided to prevent accidental full-table deletes.

Query parameters: filter or ids (at least one required)

Request body (alternative):

json
{"ids": [1, 2, 3]}

Or:

json
{
  "resource": [
    {"id": 1},
    {"id": 2}
  ]
}

Response (200):

json
{
  "meta": {
    "count": 3,
    "took_ms": 1.12
  }
}

Schema Endpoints

Introspect and modify database schemas.

GET /api/v1/{serviceName}/_schema

Get the full schema for a service, including all tables, views, columns, primary keys, foreign keys, and indexes.

GET /api/v1/{serviceName}/_schema/

Get the detailed schema for a single table.

Response (200):

json
{
  "name": "users",
  "type": "table",
  "primary_key": ["id"],
  "columns": [
    {
      "name": "id",
      "position": 1,
      "db_type": "integer",
      "go_type": "int32",
      "json_type": "integer",
      "nullable": false,
      "is_primary_key": true,
      "is_auto_increment": true,
      "is_unique": false,
      "default": "nextval('users_id_seq'::regclass)"
    },
    {
      "name": "name",
      "position": 2,
      "db_type": "character varying",
      "go_type": "string",
      "json_type": "string",
      "nullable": false,
      "max_length": 255,
      "is_primary_key": false,
      "is_auto_increment": false,
      "is_unique": false
    },
    {
      "name": "email",
      "position": 3,
      "db_type": "character varying",
      "go_type": "string",
      "json_type": "string",
      "nullable": true,
      "max_length": 255,
      "is_primary_key": false,
      "is_auto_increment": false,
      "is_unique": true
    }
  ],
  "foreign_keys": [],
  "indexes": []
}

POST /api/v1/{serviceName}/_schema

Create a new table.

Request body:

json
{
  "name": "products",
  "columns": [
    {"name": "id", "type": "serial", "is_primary_key": true},
    {"name": "name", "type": "varchar(255)", "is_nullable": false},
    {"name": "price", "type": "decimal(10,2)"},
    {"name": "created_at", "type": "timestamp", "default": "now()"}
  ]
}

Returns the schema of the newly created table (201).

PUT /api/v1/{serviceName}/_schema/

Alter an existing table. Provide a list of schema changes.

Request body:

json
{
  "changes": [
    {
      "type": "add_column",
      "column": "description",
      "definition": {"name": "description", "type": "text", "is_nullable": true}
    },
    {
      "type": "drop_column",
      "column": "old_field"
    },
    {
      "type": "rename_column",
      "column": "name",
      "new_name": "title"
    }
  ]
}

Supported change types: add_column, drop_column, rename_column, modify_column

DELETE /api/v1/{serviceName}/_schema/

Drop a table. This is irreversible.

Response (200):

json
{
  "success": true,
  "message": "Table 'products' dropped successfully"
}

Stored Procedure Endpoints

GET /api/v1/{serviceName}/_proc

List all stored procedures and functions for a service.

Response (200):

json
{
  "resource": [
    {
      "name": "calculate_total",
      "type": "function",
      "return_type": "numeric",
      "parameters": [
        {"name": "order_id", "type": "integer"}
      ]
    }
  ],
  "meta": {"count": 1}
}

POST /api/v1/{serviceName}/_proc/

Call a stored procedure with parameters.

Request body:

json
{
  "order_id": 42
}

Query parameters are also accepted and merged into the parameter map. This allows simple calls like:

bash
curl -X POST "http://localhost:8080/api/v1/mydb/_proc/calculate_total?order_id=42" \
  -H "Authorization: Bearer $TOKEN"

Response (200):

json
{
  "resource": [
    {"total": 299.99}
  ],
  "meta": {
    "count": 1,
    "took_ms": 5.67
  }
}

Per-Service OpenAPI Spec

GET /api/v1/{serviceName}/_doc

Returns an OpenAPI 3.1 specification for a single service, including schemas derived from actual table definitions.


CORS

Faucet enables CORS with the following defaults:

  • Allowed origins: *
  • Allowed methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
  • Allowed headers: Accept, Authorization, Content-Type, X-API-Key, X-Requested-With
  • Exposed headers: X-Total-Count, X-Request-ID, Link
  • Max age: 300 seconds

Rate Limiting

Request rate limiting middleware is available. See Deployment for configuration.


Request Size Limit

The maximum request body size is 10 MB by default.

Released under the MIT License.