SMLL Docs

Databases API

Provision PostgreSQL and MySQL, fetch connection strings, manage backups, replicas, forks and IP allowlists.

SMLL runs PostgreSQL (CloudNativePG) and MySQL (Percona XtraDB Cluster). The API covers provisioning, credentials, backups, restores, replicas and forks.

All paths below are relative to:

https://api.smll.io/api/v1/workspaces/{workspaceID}/vpcs/{vpcID}/databases

Endpoints

MethodPathRolePurpose
GET/MemberList databases in the VPC
POST/AdminProvision a database
GET/{databaseID}MemberDatabase detail, with live status
PATCH/{databaseID}AdminRename, resize, toggle external access
DELETE/{databaseID}AdminDelete the database
GET/{databaseID}/connectionMemberConnection details and credentials
GET/{databaseID}/backupsMemberList backups
POST/{databaseID}/backupsAdminTake a backup now
POST/{databaseID}/backups/restoreAdminRestore into a new database
PATCH/{databaseID}/backups/scheduleAdminConfigure the backup schedule
DELETE/{databaseID}/backups/{backupName}AdminDelete a backup
POST/{databaseID}/forkAdminFork to a new database
PATCH/{databaseID}/replicasAdminChange the read replica count
GET/{databaseID}/schemaAdminInspect tables and columns
POST/{databaseID}/queryAdminRun a SQL query
GET/{databaseID}/slow-queriesMemberSlow query log (accepts limit)
POST/{databaseID}/slow-queries/resetAdminClear slow query statistics
GET/{databaseID}/ip-allowlistMemberList allowlist rules
POST/{databaseID}/ip-allowlistAdminAdd a rule
DELETE/{databaseID}/ip-allowlist/{ruleID}AdminRemove a rule

Provision a database

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{
        "name": "app-db",
        "engine": "postgresql",
        "instance_type": "smll.small",
        "storage_gb": 10,
        "postgres_version": "16",
        "replicas": 1,
        "external_access_enabled": true
      }' \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases"
FieldTypeDefaultNotes
namestringrequiredUnique in the VPC, 19 characters or fewer
enginestringpostgresqlpostgresql or mysql
instance_typestringsee notessmll.nano through smll.xlarge. Defaults to smll.nano, or smll.small for MySQL
storage_gbintegerDisk size
postgres_versionstringPostgreSQL major version
mysql_versionstringMySQL version, when engine is mysql
replicasintegerSee the note below
external_access_enabledbooleanExpose the database off-platform

MySQL has two constraints PostgreSQL does not. It cannot run on smll.nano, because Galera needs more memory than that plan provides, and replicas must be an odd number (1, 3, 5) so the cluster can hold quorum. Even counts are rejected.

Provisioning is asynchronous. The response comes back with status: "creating"; poll the detail endpoint until it reads running.

Connection details

curl -s -H "$AUTH" \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/connection"

The response carries both routes to the database:

FieldMeaning
host, port, database, username, passwordIn-cluster connection parts
database_urlReady-made internal connection string
pooler_host, pooler_host_roPgBouncer endpoints (PostgreSQL)
read_only_host, read_only_urlRoute reads to a replica
external_host, external_host_roReachable from outside the platform

Use the internal host from services in the same VPC: it stays inside the network and skips the edge proxy. Use the external host from your laptop, CI, or anything off-platform.

External access has to be switched on (external_access_enabled), and is then reachable at a per-database subdomain:

postgresql://app:<password>@<shortID>.eu-central-1.database.smllapp.com:5432/<shortID>
mysql://app-<shortID>:<password>@<shortID>.eu-central-1.mysql.smllapp.com:3306/<shortID>

<shortID> is the first 8 characters of the database UUID.

The connection endpoint returns the live password in plain text, to any workspace member including Viewers. Treat a call to it as handing out full database access, and prefer wiring services up with a shared secret over passing credentials around.

Backups and restores

Take a backup on demand:

curl -s -X POST -H "$AUTH" \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/backups"

Restores do not overwrite the source. A restore provisions a new database from the backup, which is why the request takes a name:

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"app-db-restored","backup_name":"..."}' \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/backups/restore"
FieldRequiredNotes
nameYesName for the new database
backup_nameFor MySQLWhich backup to restore from
target_timeNoPoint in time to recover to, for PostgreSQL

If the name is already taken in the VPC you get 409. If no completed backup exists yet, the platform triggers one and returns 409 telling you to retry shortly.

That shape is deliberate: recovery never destroys the thing you are recovering from. Point your application at the restored database once you have checked it.

Forks

A fork gives you a second database seeded from this one, for a staging environment or a migration rehearsal:

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"name":"app-db-staging"}' \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/fork"
FieldRequiredNotes
nameYesName for the forked database
target_vpc_idNoFork into a different VPC, defaults to this one

A fork is a restore in friendlier clothes: it takes the latest backup, triggering a fresh one first if none exists, and restores it into a new cluster. So the copy reflects the last backup rather than the parent's state this instant, and a fork of a busy database can take a while. It is a fully independent database from the moment it exists, and writes to it never reach the parent.

Replicas

curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"replicas":2}' \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/replicas"

Replicas serve reads through read_only_host. Each one is a running instance of the same plan and is metered as such, so check the cost estimator before scaling up.

Queries and schema

GET /schema returns tables and columns. POST /query runs SQL and returns rows. Both require Owner or Admin, because they can read and modify any data in the database.

These back the dashboard's SQL console. For application traffic, connect directly with a normal driver rather than proxying queries through the API.

IP allowlists

With external access on, the database is reachable from the internet and is protected by its password. An allowlist narrows that to known addresses:

curl -s -X POST -H "$AUTH" -H "Content-Type: application/json" \
  -d '{"cidr":"203.0.113.4/32","description":"office"}' \
  "$BASE/workspaces/$WORKSPACE_ID/vpcs/$VPC_ID/databases/$DATABASE_ID/ip-allowlist"

An empty allowlist means no IP restriction, not a closed door. Adding the first rule is what starts the filtering, so add your own address before you add anything else, or you will lock yourself out.

Status values

StatusMeaning
creatingBeing provisioned
runningAccepting connections
errorThe cluster failed to reach a healthy state
deletingTeardown in progress
deletedSoft-deleted, excluded from lists

Status is resynced against the live cluster whenever you list or read a database, so it reflects the cluster rather than the last thing the API was told.

On this page