API · Backend (MariaDB)
MariaDB-backed Voltro backend — binlog CDC real-time across replicas plus file storage. The dogfooded shape for K8s multi-replica apps.
The notes base wired for MariaDB: store: 'mariadb' turns on the binlog CDC reader so reactive subscriptions fan out across every replica via the database's binary log — no Redis/NATS, the binlog IS the message bus. File storage is on by default. This is the local-dev mirror of the helm MariaDB production shape. Template id: api-backend-mariadb.
Scaffold
voltro create-project acme --api=api-backend-mariadb --baseline=compose-mariadbPair it with the compose-mariadb baseline (or helm): MariaDB must run with binlog_format=ROW + binlog_row_image=FULL and a user holding REPLICATION SLAVE, REPLICATION CLIENT, plus MinIO/S3 for storage. The compose-mariadb baseline sets all of this up with one pnpm db:up.
What ships
apps/acme/api/ # dir named by the app, not the template
├── app.config.ts # store:'mariadb' + storagePlugin()
├── .env.example # DB / storage / AI / auth / cluster env
├── package.json
├── tsconfig.json
├── README.md
├── database/
│ └── schema.ts # actors + tenants + notes (same as api-backend)
├── queries/
│ └── notes.query.ts + .query.server.ts
└── mutations/
└── notes.create.mutation.ts + .mutation.server.tsThe wiring
// app.config.ts
import { storagePlugin } from '@voltro/plugin-storage'
export default {
type: 'api' as const,
name: '{{capProjectName}}{{capAppName}}',
store: 'mariadb' as const, // turns on binlog CDC
plugins: [
// Blobs in object storage (MinIO locally, S3 in prod); metadata in
// _voltro_storage_refs. Provider resolved from STORAGE_PROVIDER.
storagePlugin(),
],
}The shipped app.config.ts also carries commented-out jwtBearerStrategy + apiKeyStrategy blocks under an auth: key — uncomment and configure them to wire your IdP / API keys.
Environment — .env.example
Copy to .env and adjust. The key vars:
# Database (MariaDB + binlog CDC)
DB_DIALECT=mariadb
DB_URL=mysql://app:app@localhost:3307/{{projectNameSnake}}
CDC=1 # push-based real-time across replicas (set 0 for inline-only)
# File storage — MinIO locally, S3 in prod
STORAGE_PROVIDER=minio
S3_ENDPOINT=http://localhost:9000
S3_BUCKET={{projectNameSnake}}
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_FORCE_PATH_STYLE=1
# Auth — HMAC key for signed sessions (REQUIRED in production)
VOLTRO_SESSION_SECRET=dev-only-change-me
# Cluster (K8s, >1 replica) — advertise a routable host so a workflow
# can resume on another pod. Inject POD_IP via the downward API in prod.
# VOLTRO_WORKFLOW_RUNNER_HOST=Why MariaDB CDC
Behind a load balancer each live client WebSocket lives on one replica. A mutation runs on one replica; without cross-instance fan-out, other replicas' clients go stale. MariaDB's ROW-format binlog is one of the two dialects (with postgres LISTEN/NOTIFY) that observe writes across instances natively — CDC=1 reads the binlog and injects remote change events into every replica's store. With CDC=0 it falls back to single-process inline emit.
Boot
pnpm install
cp .env.example .env # then adjust DB_URL / secrets
# Start MariaDB (binlog ROW) + MinIO — the compose-mariadb baseline does this:
pnpm db:up
pnpm --filter @acme/api devvoltro dev against MariaDB auto-migrates the schema before any handler boots, and the boot log confirms CDC: binlog CDC (ROW).
Pairs well with
- The
compose-mariadbbaseline — local MariaDB + MinIO. - The
helmbaseline — the K8s production target (per-podserver_id+ workflow-runner host).
See also
api-backend— the base this builds on.@voltro/plugin-storage— the storage plugin wired here.