Dyrected
Deployment & OperationsInfrastructureDatabase Adapters

MySQL

Connect a self-hosted Dyrected app to MySQL, a relational adapter that behaves like Postgres.

MySQL is a solid relational choice when it's the database you already run. Install @dyrected/db-mysql, point it at your server, and pass it as the db key in dyrected.config.ts. It behaves like the Postgres adapter: the same tables, the same promoted columns, the same transactions.

import { defineConfig } from '@dyrected/core'
import { mysqlAdapter } from '@dyrected/db-mysql'

export default defineConfig({
  db: mysqlAdapter({ url: process.env.DATABASE_URL! }),
  collections: [],
  globals: [],
})

Set DATABASE_URL to a MySQL connection string, for example mysql://user:[email protected]:3306/my_app. On startup Dyrected connects and prepares the schema.

Dyrected Cloud manages the database for you. You only configure a db adapter when you self-host. Cloud projects normally omit db. See the Database overview.

Configuration

Give the adapter either a full connection url or the individual connection fields:

// Connection URL
mysqlAdapter({ url: process.env.DATABASE_URL! })

// Individual fields
mysqlAdapter({
  host: process.env.MYSQL_HOST,
  port: 3306,
  user: process.env.MYSQL_USER,
  password: process.env.MYSQL_PASSWORD,
  database: process.env.MYSQL_DATABASE,
})
OptionDescription
urlFull MySQL connection string. Provide this or the fields below.
hostServer host. Defaults to localhost.
portServer port. Defaults to 3306.
userConnection user.
passwordConnection password.
databaseDatabase name to use.

Prefer an environment-provided url when your host rotates credentials or manages connection details for you.

Connecting to a local server? Use 127.0.0.1, not localhost. On some systems localhost resolves to IPv6 (::1) while MySQL only listens on IPv4, which fails to connect. If you hit an EADDRNOTAVAIL error, switch the host to 127.0.0.1.

What happens on startup

The first time your app connects, the adapter creates a table for each collection that doesn't have one yet, plus an internal table for globals. Adding a new collection or field usually works on your next boot with no extra step. See Migrations for how schema changes are applied over time.

As a local-development convenience, if the database named in your connection doesn't exist yet, the adapter will create it (CREATE DATABASE IF NOT EXISTS). In production, create the database ahead of time rather than relying on this.

How your data is stored

Each collection maps to a table named collection_<slug>. Every document is stored as a JSON data column alongside id, created_at, and updated_at. Globals live in an internal key/value table.

Because the document body is JSON, you can filter and sort on any field without declaring it first. Fields you mark with promoted: true are also lifted into their own real, typed columns so the database can work with them directly. See Indexes for when to promote a field.

Transactions and raw SQL

The MySQL adapter runs multi-step writes as real database transactions, and workflow transitions rely on this. Reads issued inside a transaction take row locks, so concurrent writers wait their turn. See Transactions.

For advanced cases, the MySQL adapter can also run raw SQL. Reach for collections, fields, and the query APIs first, and treat raw SQL as an escape hatch.

Deploying to production

MySQL is a good production adapter when it's already part of your stack. As with any relational adapter, provide the connection string through an environment variable, use your provider's connection pooler where recommended, and remember that startup schema sync is additive — it creates missing tables and columns but never drops or retypes existing ones. Back up before a schema change. See Migrations and Deployment.

On this page

Dyrected| Cloud

Get your backend ready in minutes

Use a managed database, storage, APIs, and admin dashboard without setting up the infrastructure yourself.

Set Up My Backend