Voltro + PlanetScale
Connect Voltro to PlanetScale MySQL — the Vitess foreign-key caveat that affects reference() cascades, the connection string, TLS, and branch workflows.
PlanetScale is MySQL on Vitess, so it uses Voltro's mysql dialect. It connects fine and the schema DSL, queries, and workflows work — but there's a critical caveat about foreign keys you must address before relying on reference() cascades or FK auto-indexes.
The foreign-key caveat (read this first)
Vitess disables foreign keys by default. Voltro's reference() column emits a real SQL FOREIGN KEY constraint, and several framework behaviors hang off that constraint:
onDelete: 'cascade'/'restrict'/'setNull'— enforced by the FK. With FKs disabled, deleting a parent row does NOT cascade or get blocked; orphaned children just stay.- FK auto-index — Voltro creates a B-tree index on every FK column by default. That part (the index) still works, but the referential integrity it pairs with does not.
You have two options:
- Enable foreign-key support on the PlanetScale branch (PlanetScale supports FKs when the keyspace is configured for it — turn it on in the branch settings). Then
reference()behaves as documented. This is the recommended path. - Accept application-level integrity — if you can't enable FKs, treat
reference()as a plain indexed column and enforce cascade/restrict semantics in your mutation handlers yourself. Don't rely ononDelete:doing anything.
Decide this up front — a schema built assuming cascades that silently don't fire is a data-integrity bug waiting to surface.
Connect
DB_DIALECT=mysql
DB_URL=mysql://[USER]:[PASSWORD]@[HOST].[REGION].psdb.cloud:3306/[DB]?ssl-mode=REQUIREDPlanetScale requires TLS — ssl-mode=REQUIRED. The mysql2 driver Voltro uses honors it. PlanetScale's host is region-specific (*.psdb.cloud).
Reactivity — inline by default, cross-instance via the broadcast bus
Voltro's mysql dialect emits change events inline: the writing process emits them in-process. There is no binlog consumer in the mysql dialect, and PlanetScale gives no binlog access anyway (Vitess hides it behind the proxy). So:
- Reactivity works within a single instance out of the box. One Voltro process sees its own writes and pushes them to its subscribers. Nothing to enable — it's the default.
- Cross-instance reactivity needs a pub/sub bus. A write on instance A is invisible to subscriptions held by instance B unless you add
@voltro/plugin-broadcast(Redis / NATS). The bus fans out each app-mutation change event to every replica, closing the gap binlog CDC can't reach on Vitess.
// app.config.ts
import { broadcastPlugin } from '@voltro/plugin-broadcast'
export default { type: 'api' as const, name: 'api', plugins: [broadcastPlugin()] }
// BROADCAST_URL=redis://… (or nats://…)Caveat: the bus carries app-mutation changes (writes through ctx.store), not out-of-band DB writes — see Multi-replica reactivity. For PlanetScale (no binlog), the bus is the only cross-instance path; if you need out-of-band-write reactivity too, postgres is the right backend.
MySQL dialect behavior
Beyond the FK caveat and the cross-instance-reactivity note above, the standard MySQL-dialect differences apply (the framework handles them for you):
- No
RETURNING— INSERT-then-SELECT under the hood. - No foreign keys by default — see the foreign-key caveat above; this is the one you must address explicitly.
0/1booleans,JSONcolumn type, backtick identifier quoting.
See the MySQL dialect page for the full surface.
Branching
PlanetScale branches are schema-isolated database copies — a good fit for Voltro preview deploys. Point a preview at a branch's connection string; voltro dev / voltro start auto-migrates the schema on first boot. Note the FK setting is per-branch — enable it on each branch that needs cascades.
Verify
[voltro:dev] sql dialect resolved: mysql — CDC: inline only (no binlog CDC), RETURNING: INSERT/UPDATE/DELETE then SELECT (no RETURNING)
[voltro:dev] workflow engine: cluster-sql, dialect=mysql, runnerStorage=sqlConfirm with voltro logs --tail 50. To verify FK behavior, delete a parent row and check whether the configured onDelete actually fired — if it didn't, foreign keys are off on the branch.
See also
- MySQL dialect — RETURNING gaps, inline CDC, mysql2 quirks.
- Multi-replica reactivity — cross-instance change fan-out via
@voltro/plugin-broadcast. - Mixins and the
reference()docs for what cascades the FK is supposed to enforce.