Guide

Get started & deploy

Try dbAPI locally in about five minutes, then run the published container against your own MySQL or MariaDB.

  • Quick start: Docker + the repository
  • Production: ghcr.io/dbapiator/dbapi

Quick start

From zero to your first JSON:API responses with the local Docker stack.

1. Clone the repository

shell
git clone https://github.com/dbAPIator/dbapi.git
cd dbapi

2. Start dbAPI

From the repository root:

shell
docker compose up -d

Wait until the container is healthy, then:

shell
curl -sS http://localhost:8888/health

Expect {"status":"ok","service":"dbAPI"}. The stack runs in single mode: one API (default), data under /v1/data/....

3. List a resource

Tables in the demo database are API resources. List customers:

shell
curl -sS 'http://localhost:8888/v1/data/customers?page[limit]=2' | jq .

You get a JSON:API document: data[] with type, id, attributes (columns), and relationships (foreign keys / children).

Fetch one row:

shell
curl -sS http://localhost:8888/v1/data/customers/1 | jq .

4. Filter and include

shell
curl -sS 'http://localhost:8888/v1/data/customers?filter=country_code=US&page[limit]=5' | jq .

curl -sS 'http://localhost:8888/v1/data/customers/1?include=orders' | jq .

Field and relationship names come from your schema — check OpenAPI before guessing:

http://localhost:8888/swagger.html?url=v1/swagger

5. Create a record (optional)

shell
curl -sS -X POST http://localhost:8888/v1/data/customers \
  -H 'Content-Type: application/vnd.api+json' \
  -d '{
    "data": {
      "type": "customers",
      "attributes": {
        "name": "Five Minute Co",
        "email": "five@example.com",
        "country_code": "RO"
      }
    }
  }' | jq .

The local stack starts with auth mode none so you can explore without a JWT. Production setups usually use dbAuth — see the authentication tutorial on GitHub.

Docker deployment

Run dbAPI from the published container without cloning the repo or building PHP locally. Images ship to GitHub Container Registry on each release tag.

For live code mounts and a bundled MariaDB/Redis stack, use the quick start above (docker compose up -d in the repository). For consumer projects, copy docker/base/ from the repo into your app.

Image location and tags

Registryghcr.io
Imageghcr.io/dbapiator/dbapi
Packageghcr.io/dbapiator/dbapi
TagMeaning
latestMost recent release
1.5.0Exact version (pin in production)
1.5Latest patch in 1.5.x
1Latest release in major 1.x.x

Pin a version in production rather than latest.

Pull the image

Public package

shell
docker pull ghcr.io/dbapiator/dbapi:1.5.0

Private package

shell
echo "$GITHUB_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
docker pull ghcr.io/dbapiator/dbapi:1.5.0

Use a GitHub personal access token with the read:packages scope.

Deployment modes

ModeDEPLOYMENT_MODEData planeAPI id
Single-API single /v1/data/{resource} default (fixed)
Multi-API multi (default) /v1/apis/{apiId}/data/{resource} Per API

Single-API mode

On container start the entrypoint waits for MySQL/MariaDB, scaffolds the default API under CONFIGS_DIR, pre-fills connection from DB_*, then tests, builds schema, and activates when the database is reachable. No manual Management API call is required for a first run.

Inspect status with GET /mgmt/v1. OpenAPI for management: /management-openapi-single.yaml.

Multi-API mode

The container starts the web server only. Create each API through the Management API and keep configs on a persistent volume at CONFIGS_DIR.

shell
curl -sS -X POST 'http://localhost:8888/mgmt/v1/apis?provision=immediate' \
  -H 'Content-Type: application/json' \
  -H 'X-Management-Key: YOUR_SECRET' \
  -d '{
    "name": "demo",
    "connection": {
      "driver": "mysql",
      "host": "mysql.example.com",
      "port": 3306,
      "database": "myapp",
      "username": "user",
      "password": "password"
    }
  }'

Data endpoints: http://localhost:8888/v1/apis/demo/data/{resource}

Requirements

ComponentRequiredNotes
MySQL or MariaDBYesvia mysqli
Writable config dirYesVolume at CONFIGS_DIR
RedisNoOnly for webhook dispatch

The container listens on port 80 internally. Map it with -p 8888:80.

Environment variables

Core

VariableRequiredDefaultDescription
CONFIGS_DIRRecommendedinternal pathUse /app/apis in Docker
CONFIG_API_SECRETYes (prod)dev defaultHeader X-Management-Key
CONFIG_API_IPS_ACLSNoallow allJSON IP ACL for management
DEPLOYMENT_MODENomultiSet single for auto-provision

Single-mode database

VariableRequiredDefault
DB_HOSTYes
DB_PORTNo3306
DB_NAMEYes
DB_USERYes
DB_PASSWORDNoempty

Optional metadata: API_TITLE, API_DESCRIPTION, API_VERSION, license and contact API_* vars.

Limits (defaults)

DEFAULT_PAGE_SIZE=100, MAX_PAGE_SIZE=1000, REQUEST_TIMEOUT_SECONDS=60, MAX_INCLUDE_DEPTH=5.

Redis (optional webhooks)

REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_STREAM, REDIS_GROUP.

Single-API: docker run

shell
docker run -d --name dbapi \
  -p 8888:80 \
  -e DEPLOYMENT_MODE=single \
  -e CONFIGS_DIR=/app/apis \
  -e CONFIG_API_SECRET='change-me-in-production' \
  -e DB_HOST=mysql.example.com \
  -e DB_PORT=3306 \
  -e DB_NAME=myapp \
  -e DB_USER=dbapi \
  -e DB_PASSWORD='secret' \
  -v dbapi-configs:/app/apis \
  ghcr.io/dbapiator/dbapi:1.5.0

Verify:

shell
curl -sS http://localhost:8888/
curl -sS http://localhost:8888/v1/data/
curl -sS http://localhost:8888/mgmt/v1/apis/default \
  -H 'X-Management-Key: change-me-in-production'

Swagger UI: http://localhost:8888/swagger.html?url=v1/swagger

Single-API: Docker Compose

Use the starter in the repo under docker/base/:

shell
cp -r path/to/dbapi/docker/base ./docker/dbapi
cd docker/dbapi
cp .env.example .env   # CONFIG_API_SECRET, DB_*, DBAPI_IMAGE_TAG
docker compose up -d

Replace secrets and pin the image tag before production. Optional Adminer: docker compose --profile tools up -d.

Multi-API: docker run

shell
docker run -d --name dbapi \
  -p 8888:80 \
  -e CONFIGS_DIR=/app/apis \
  -e CONFIG_API_SECRET='change-me-in-production' \
  -v dbapi-configs:/app/apis \
  ghcr.io/dbapiator/dbapi:1.5.0

Create APIs via the Management API.

Volumes and persistence

Mount /app/apis (or your CONFIGS_DIR) as a named volume. It holds per-API config, policies, and OpenAPI. Application code is in the image — do not mount src/. Back up the config volume; deleting it removes API definitions.

Upgrades

  1. Pin semver tags in production.
  2. Read the changelog.
  3. Pull the new tag and recreate the container; keep the config volume.
shell
docker pull ghcr.io/dbapiator/dbapi:1.5.0
docker stop dbapi && docker rm dbapi
# re-run docker run or compose with the new tag

Health check

GET /health is an unauthenticated liveness probe ({"status":"ok","service":"dbAPI"}). It does not test the database — use Management API connection:test for that.

shell
curl -fsS http://localhost:8888/health

Troubleshooting

SymptomLikely causeCheck
denied on pullPrivate GHCRdocker login or public package
Exits on startMySQL unreachableDB_HOST, network, depends_on
404 on data routesAPI inactive / wrong modeSingle: /mgmt/v1/apis/default
401 on managementWrong secretCONFIG_API_SECRETX-Management-Key
Empty config after restartNo volumeMount /app/apis
shell
docker logs -f dbapi