Skip to content
rdlabo.devdocs

Data Layer

Standalone MySQL / Hyperdrive access for Workers, plus the thin Hono container adapter. Fixed
+09:00 storage helpers are independent of IANA display timezones.

Import database helpers from @rdlabo/workers-mysql. The package installs mysql2 directly. Add
drizzle-orm only when using the /drizzle or /testing entry point. The old
@rdlabo/workers-hono-kit/db path is a deprecated compatibility re-export.

Workers using mysql2 must enable its required Node.js networking APIs:

# wrangler.toml
compatibility_flags = ["nodejs_compat"]
npm install @rdlabo/workers-mysql drizzle-orm
npm install -D @types/node@20

For candidate tarball installation, see Development.

Hyperdrive database

createHyperdriveDatabase() lazily opens primary and replica connections from Hyperdrive bindings.
For read/write paths, retry boundaries, and invocation lifetime, see
Workers MySQL Runtime.

import { createHyperdriveDatabase } from '@rdlabo/workers-mysql';
import { DRIZZLE_ORM_OPTIONS } from '@rdlabo/workers-mysql/drizzle';
import { drizzle } from 'drizzle-orm/mysql2';

const db = createHyperdriveDatabase({
  primaryHyperdrive: env.DB_PRIMARY,
  replicaHyperdrive: env.DB_REPLICA,
  createOrm: (primary) => drizzle(primary, { schema, ...DRIZZLE_ORM_OPTIONS }),
});

const rows = await db.read<Item>('SELECT * FROM items WHERE id = ?', [id]);
const freshRows = await db.query<Item[]>('SELECT * FROM items WHERE id = ?', [id]);
await db.write((dz) => dz.insert(items).values(input));
await db.transaction((tx) => tx.insert(items).values(input));

const snapshot = await db.readTransaction(async ({ orm, query }) => ({
  items: await orm.select().from(items),
  count: await query<{ count: number }[]>('SELECT COUNT(*) count FROM items'),
}));

MySQL enforces READ ONLY for every transaction attempt. Drizzle does not provide a distinct read-only transaction type, so applications can wrap orm in a SELECT-only facade when they also want compile-time enforcement.

Do not call readTransaction() recursively from inside its callback. Calls share one serialized snapshot lane, so a nested call would wait for its own outer transaction to finish. Consumers that expose nested snapshot helpers should reuse the outer reader instead.

Use hyperdriveConnectionOptions() when constructing lower-level mysql2 connections. The default JavaScript date conversion timezone is +09:00; it does not change the MySQL session timezone.

Hono applications that want the standard request container use the thin adapter separately:

import { createContainerRuntime } from '@rdlabo/workers-hono-kit/mysql';

Writes and retries

  • retryWhenDeadlock() retries ER_LOCK_DEADLOCK with a wait of delay × attempt between attempts.
  • insertIdOf(), affectedRowsOf(), and insertedIdsOf() normalize mysql2 write results.
  • withMysqlConnections() opens primary and replica connections in parallel for a scoped operation.

Drizzle and JST helpers

Use jstTimestamp, jstDatetime, and jstDate for shared date behavior. Pair update timestamps with jstOnUpdateNow() because custom timestamp types do not expose Drizzle's .onUpdateNow(). For decimal columns, use Drizzle's decimal(name, { precision, scale, mode: 'number' }) directly.

Generic business-time conversion is separate from the DB's fixed +09:00 wire contract.
Install @rdlabo/workers-timezone directly and migrate from the kit's deprecated /business-time
compatibility path to its canonical entry point.

npm install @rdlabo/workers-timezone
import { addBusinessDays, toBusinessDateTime } from '@rdlabo/workers-timezone';

toBusinessDateTime(new Date('2026-07-05T21:00:00Z'));
// '2026-07-06 06:00:00'

addBusinessDays('2026-07-06', 3);
// '2026-07-09'

Migrating from workers-hono-kit

The package boundary is a breaking change in 0.12.0. Update these imports before
upgrading:

Current import Replacement
createContainerRuntime from the kit root @rdlabo/workers-hono-kit/mysql
retryWhenDeadlock from the kit root @rdlabo/workers-mysql
DB helpers from @rdlabo/workers-hono-kit/db @rdlabo/workers-mysql, /drizzle, or /migrations
DB test helpers from @rdlabo/workers-hono-kit/testing @rdlabo/workers-mysql/testing

The old /db and DB-related /testing exports remain available for backward compatibility.
Their individual functions and types carry @deprecated notices pointing to the standalone
package. No removal release is scheduled. The kit-owned /mysql adapter is not deprecated.
Because /testing statically re-exports DB helpers, all kit /testing consumers must install the
MySQL package and drizzle-orm, including consumers of non-DB helpers such as Firebase or KV fakes.

Next step

Continue to Realtime and Offline, or see
@rdlabo/workers-mysql for the
standalone package guides.