You're reading docs for Nextly Alpha. APIs may change between releases.

Database

MySQL

Set up Nextly with MySQL. Covers installation, configuration, connection pooling, SSL, Docker setup, and MySQL-specific limitations.

MySQL is supported for teams with existing MySQL infrastructure. The adapter uses the mysql2 driver and handles MySQL-specific differences like the lack of a RETURNING clause automatically.

Installation

Install the MySQL adapter alongside its peer dependency:

@nextlyhq/adapter-mysql mysql2

Configuration

Add the adapter to your nextly.config.ts:

nextly.config.ts
import { defineConfig } from '@nextlyhq/nextly';

export default defineConfig({
  database: {
    adapter: 'mysql',
    url: process.env.DATABASE_URL,
  },
});

Programmatic Usage

You can also create the adapter directly:

adapter.ts
import { createMySqlAdapter } from '@nextlyhq/adapter-mysql';

const adapter = createMySqlAdapter({
  url: process.env.DATABASE_URL!,
});

await adapter.connect();

Connection String

MySQL connection strings follow this format:

mysql://user:password@host:port/database

For example:

.env
DATABASE_URL=mysql://nextly:nextly@localhost:3306/nextly_dev

You can also specify connection details individually:

nextly.config.ts
export default defineConfig({
  database: {
    adapter: 'mysql',
    host: 'localhost',
    port: 3306,
    database: 'nextly_dev',
    user: 'nextly',
    password: 'nextly',
  },
});

Connection Pool

The adapter uses mysql2's built-in connection pooling. Default settings:

OptionDefaultDescription
pool.max10Maximum connections in the pool.
pool.idleTimeoutMs30000Close idle connections after 30 seconds.
pool.connectionTimeoutMs10000Timeout when establishing a new connection.

To customize:

nextly.config.ts
export default defineConfig({
  database: {
    adapter: 'mysql',
    url: process.env.DATABASE_URL,
    pool: {
      max: 20,
      idleTimeoutMs: 30000,
      connectionTimeoutMs: 10000,
    },
  },
});

SSL Configuration

For production databases, enable SSL:

nextly.config.ts
export default defineConfig({
  database: {
    adapter: 'mysql',
    url: process.env.DATABASE_URL,
    ssl: {
      rejectUnauthorized: true,
      ca: process.env.CA_CERT,
    },
  },
});

MySQL-Specific Options

These options are unique to the MySQL adapter:

OptionTypeDefaultDescription
charsetstringutf8mb4Character set for the connection.
timezonestring'local'Timezone for date handling.
multipleStatementsbooleanfalseAllow multiple SQL statements per query. Disabled by default for security.
dateStringsbooleanfalseReturn dates as strings instead of Date objects.
queryTimeoutMsnumber15000Default timeout for the adapter's executeWithTimeout() method.
nextly.config.ts
export default defineConfig({
  database: {
    adapter: 'mysql',
    url: process.env.DATABASE_URL,
    charset: 'utf8mb4',
    timezone: '+00:00',
  },
});

Differences from PostgreSQL

The MySQL adapter handles several differences automatically, but they are worth knowing:

  • No RETURNING clause -- After INSERT and UPDATE operations, the adapter performs an extra SELECT to return the affected rows. This is transparent to your application code.
  • No savepoints -- Nested transactions are not supported. Savepoint methods are disabled for safety.
  • No ILIKE -- Case-insensitive search uses LOWER(column) LIKE LOWER(value) as a fallback. This happens automatically when you use the ILIKE operator in your queries.
  • No JSONB -- MySQL has a native JSON type but lacks PostgreSQL's JSONB with its indexing and operators.
  • No array types -- Arrays must be stored as JSON.
  • Upsert syntax -- Uses ON DUPLICATE KEY UPDATE instead of PostgreSQL's ON CONFLICT DO UPDATE. The adapter handles this transparently.
  • Backtick identifiers -- MySQL uses backticks for escaping identifiers instead of double quotes.
  • ? placeholders -- MySQL uses ? for query parameters instead of $1, $2.

Deadlock Retry

The adapter automatically retries transactions that fail with deadlock errors (MySQL error 1213). Configure retry behavior with transaction options:

await adapter.transaction(
  async (tx) => {
    await tx.insert('orders', orderData);
    await tx.update('inventory', stockUpdate, where);
  },
  {
    retryCount: 3,
    retryDelayMs: 100, // Exponential backoff: 100ms, 200ms, 300ms
  }
);

Docker Setup for Local Development

Run MySQL locally with Docker:

docker run -d --name nextly-mysql \
  -e MYSQL_ROOT_PASSWORD=root \
  -e MYSQL_USER=nextly \
  -e MYSQL_PASSWORD=nextly \
  -e MYSQL_DATABASE=nextly_dev \
  -p 3306:3306 \
  mysql:8

Then set your environment variable:

.env
DATABASE_URL=mysql://nextly:nextly@localhost:3306/nextly_dev

To stop and remove the container:

docker stop nextly-mysql && docker rm nextly-mysql

Capabilities

The MySQL adapter reports the following capabilities:

{
  dialect: 'mysql',
  supportsJsonb: false,
  supportsJson: true,
  supportsArrays: false,
  supportsGeneratedColumns: true,
  supportsFts: true,
  supportsIlike: false,
  supportsReturning: false,
  supportsSavepoints: false,
  supportsOnConflict: true,
  maxParamsPerQuery: 65535,
  maxIdentifierLength: 64,
}

Next Steps