Skip to content

data-table-mysql

The data-table-mysql package provides a MySQL adapter for the data-table query toolkit. It uses the mysql2 driver.

Installation

bash
npm install remix mysql2

Or install the standalone package:

bash
npm install @remix-run/data-table-mysql mysql2

Import

ts
import { createMysqlDatabaseAdapter } from 'remix/data-table-mysql'

Or from the standalone package:

ts
import { createMysqlDatabaseAdapter } from '@remix-run/data-table-mysql'

API

createMysqlDatabaseAdapter(client, options?)

Creates a DatabaseAdapter for use with createDatabase().

ts
import mysql from 'mysql2/promise'
import { createMysqlDatabaseAdapter } from 'remix/data-table-mysql'
import { createDatabase } from 'remix/data-table'

let pool = mysql.createPool({ uri: process.env.DATABASE_URL })
let adapter = createMysqlDatabaseAdapter(pool)
let db = createDatabase(adapter)

Parameters

ParameterTypeDescription
clientPool | Connection | PoolConnection (from mysql2/promise)A mysql2 connection pool or individual connection.
optionsMysqlDatabaseAdapterOptionsOptional adapter configuration.

MysqlDatabaseAdapterOptions

OptionTypeDescription
capabilitiesAdapterCapabilityOverridesOverride detected adapter capabilities.

Capabilities

The MySQL adapter reports the following capabilities by default:

CapabilityDefaultDescription
returningfalseMySQL does not support RETURNING clauses (use returnRow: true with a follow-up SELECT).
savepointstrueMySQL supports savepoints for nested transactions.
upserttrueMySQL supports ON DUPLICATE KEY UPDATE.
transactionalDdlfalseMySQL does not support DDL inside transactions (DDL causes an implicit commit).
migrationLocktrueMySQL supports GET_LOCK() for migration safety.

MySQL-Specific Considerations

  • No RETURNING: MySQL does not support RETURNING clauses. When you use db.create(table, values, { returnRow: true }), the adapter inserts the row and then performs a follow-up SELECT using LAST_INSERT_ID().
  • DDL and transactions: DDL statements (CREATE TABLE, ALTER TABLE, etc.) cause an implicit commit in MySQL. Migrations cannot be wrapped in a single transaction.
  • ILIKE: MySQL does not have ILIKE. The adapter translates ilike queries to LIKE (MySQL's LIKE is case-insensitive for case-insensitive collations by default).
  • Boolean columns: MySQL stores booleans as TINYINT(1). The adapter handles conversion.
  • Connection pooling: Always use mysql.createPool() in production rather than individual connections.
  • Enum columns: MySQL has native ENUM types. The c.enum() builder uses them.
  • Auto-increment: Use c.integer().primaryKey().autoIncrement() for MySQL auto-increment columns.

Example

ts
import mysql from 'mysql2/promise'
import { createMysqlDatabaseAdapter } from 'remix/data-table-mysql'
import { createDatabase, table, column as c } from 'remix/data-table'

let pool = mysql.createPool({
  uri: process.env.DATABASE_URL,
  waitForConnections: true,
  connectionLimit: 10,
})

let adapter = createMysqlDatabaseAdapter(pool)
let db = createDatabase(adapter)

let users = table({
  name: 'users',
  columns: {
    id: c.integer().primaryKey().autoIncrement().unsigned(),
    email: c.varchar(255).notNull().unique('users_email_uq'),
    name: c.varchar(255).notNull(),
    role: c.enum(['admin', 'user']).notNull().default('user'),
    createdAt: c.timestamp().notNull().defaultNow(),
    updatedAt: c.timestamp().notNull().defaultNow(),
  },
  timestamps: true,
})

let allUsers = await db.findMany(users, {
  orderBy: ['createdAt', 'desc'],
  limit: 20,
})

Released under the MIT License.