Skip to content

Database

This section requires authentication.

Please refer to the Authentication section for more details.

Overview

The Database API lets mobile SDK consumers execute SQL queries against a SQLite-compatible database linked to their app via the AppAmbit dashboard. Each app can have one linked database at a time; a single database can be shared across multiple apps belonging to the same team.


Authentication

All endpoints require a consumer Bearer token obtained from GET /api/consumer/token. The token is scoped to an app via the app_key claim.

Header Value
Authorization Bearer {YOUR_AUTH_KEY}
Content-Type application/json
Accept application/json

Base URL

/api/db

Execute Single Query

Execute a single SQL statement against the app's linked database.

POST/api/db/query

Body Parameters

Required Parameters

Field Type Description
sql string The SQL statement to execute

Optional Parameters

Field Type Description
params array Positional or named parameters for the statement

Example Request

JSON
{
  "sql": "SELECT id, name FROM users WHERE active = ?",
  "params": [1]
}

Example Response

JSON
{
  "results": [
    {
      "columns": ["id", "name"],
      "rows": [[1, "Alice"], [2, "Bob"]],
      "rows_read": 2,
      "rows_written": 0
    }
  ],
  "request_id": "550e8400-e29b-41d4-a716-446655440000"
}

Response Fields

Top-level:

Field Type Description
results array Array with one result object per executed statement
request_id string (UUID) Unique identifier for this request, useful for tracing

Result object:

Field Type Description
columns string[] Column names in result order
rows array[] Array of row arrays; values are typed (integer, float, string, or null)
rows_read integer Number of rows read from storage
rows_written integer Number of rows written to storage
error string Present only when the statement produced a database-level error; HTTP status remains 200

Execute Batch Statements

Execute multiple SQL statements in a single request. Optionally wraps them in a transaction.

POST/api/db/batch

Body Parameters

Required Parameters

Field Type Description
statements array Array of statement objects (minimum 1)
statements[].sql string The SQL statement

Optional Parameters

Field Type Description
statements[].params array Positional or named parameters for this statement
transaction boolean When true, wraps all statements in BEGIN / COMMIT. Default: false

Example Request

JSON
{
  "statements": [
    {
      "sql": "INSERT INTO users (name, email) VALUES (?, ?)",
      "params": ["Alice", "alice@example.com"]
    },
    {
      "sql": "UPDATE stats SET user_count = user_count + 1"
    }
  ],
  "transaction": true
}

Example Response

JSON
{
  "results": [
    {
      "columns": [],
      "rows": [],
      "rows_read": 0,
      "rows_written": 1
    },
    {
      "columns": [],
      "rows": [],
      "rows_read": 1,
      "rows_written": 1
    }
  ],
  "request_id": "550e8400-e29b-41d4-a716-446655440001"
}
Using transaction: true

BEGIN and COMMIT are injected server-side — do not include them manually in statements. If any statement contains a forbidden query, the entire batch is rejected before any statement reaches the database.


Error Responses

All errors return a JSON object with error (machine-readable code) and message (human-readable description).

403 Forbidden — Database Not Linked

The app has no database linked. Link one from the AppAmbit dashboard.

JSON
{
  "error": "database_not_linked",
  "message": "This app isn't linked to a database. Link it in your AppAmbit dashboard."
}

503 Service Unavailable — Database Unavailable

The linked database exists but is not yet active (e.g., still provisioning).

JSON
{
  "error": "database_unavailable",
  "message": "The database is not yet available. Please try again shortly."
}

403 Forbidden — Forbidden SQL

The SQL statement contains a blocked keyword or does not start with an allowed prefix.

JSON
{
  "error": "forbidden_query",
  "message": "Forbidden SQL statement: ATTACH DATABASE \"evil.db\" AS evil"
}

500 Internal Server Error — Query Execution Error

An unexpected error occurred when communicating with the database backend.

JSON
{
  "error": "query_error",
  "message": "<error detail from the database>"
}

422 Unprocessable Entity — Validation Error

Missing required fields. Standard Laravel validation error format.

JSON
{
  "message": "The sql field is required.",
  "errors": {
    "sql": ["The sql field is required."]
  }
}

SQL Restrictions

The API enforces a server-side allowlist and blocklist before forwarding SQL to the database.

Allowed Statement Prefixes

SELECT, INSERT, UPDATE, DELETE,
CREATE TABLE, CREATE INDEX,
ALTER TABLE,
DROP TABLE, DROP INDEX,
BEGIN, COMMIT, ROLLBACK

Blocked Keywords

The following keywords are blocked anywhere in the statement:

ATTACH, DETACH, PRAGMA, VACUUM,
LOAD_EXTENSION, SAVEPOINT, RELEASE

Statements not starting with an allowed prefix, or containing a blocked keyword, return 403 forbidden_query without touching the database.


Database Lifecycle

A database linked to an app transitions through the following statuses:

Status Queryable Description
pending No Provisioning requested, not yet started
provisioning No Being created in the backend
active Yes Ready to accept queries
suspended No Temporarily suspended
deleting No Deletion in progress
deleted No Permanently removed

Only active databases accept queries. All other statuses return 503 database_unavailable.


Per-Statement Errors

Database-level errors (e.g., syntax errors, constraint violations) are surfaced inside the results array with HTTP 200. Always inspect results[n].error on every result when doing error-sensitive work.

JSON
{
  "results": [
    {
      "columns": [],
      "rows": [],
      "rows_read": 0,
      "rows_written": 0,
      "error": "syntax error near \"SELECT\""
    }
  ],
  "request_id": "550e8400-e29b-41d4-a716-446655440002"
}

Notes

  • The database uses SQLite-compatible syntax. Follow the SQLite SQL dialect when writing queries.
  • One database can be shared across multiple apps belonging to the same team; each app can have only one linked database at a time.
  • Row values are type-coerced: integers and floats become JSON numbers, null becomes JSON null, and all other values become strings.