MySQL
Server-side MySQL 8 persistence with the Chatpack MySQL adapter.
@chatpack/adapter-mysql provides full Chatpack persistence through Drizzle
ORM's MySQL dialect and the mysql2 driver.
Install
pnpm add @chatpack/core @chatpack/adapter-mysql drizzle-orm mysql2Configure
import mysql from "mysql2/promise";
import { drizzle } from "drizzle-orm/mysql2";
import { chatpack } from "@chatpack/core";
import { migrationStatements, mysqlAdapter } from "@chatpack/adapter-mysql";
const pool = mysql.createPool(process.env.DATABASE_URL!);
for (const statement of migrationStatements) await pool.query(statement);
export const chat = chatpack({ storage: mysqlAdapter(drizzle(pool)), auth });Create the pool and apply migrations in server-only application code. Never expose the pool or adapter to browser code.
Support boundary
The supported target is MySQL 8 with Drizzle mysql2 and a transaction-capable
server-side connection or pool. This package does not claim MariaDB, Aurora,
PlanetScale, HTTP/serverless MySQL drivers, read replicas, or edge-runtime
support. Verify each alternative against its real driver and database before
deployment.
Migrations
The package exports chatpackSchema, all twelve tables, migrationSql, and
migrationStatements. Use migrationStatements for one-statement-per-call
drivers. Each table includes its indexes, so rerunning statements is safe for
a compatible existing schema. IF NOT EXISTS does not repair an incompatible
table; inspect such a database and perform an explicit migration. Run
backfillMessageSearchTokens(db) after importing messages from before search
tokens existed.
Correctness
- MySQL's nullable unique
pair_keyallows unlimited group rows withNULLwhile concurrent non-NULL direct pairs converge on one row. - The migration uses binary UTF-8 collation so opaque IDs and canonical search tokens remain exact instead of acquiring collation-specific matches.
- Group and initial participant insertion use one transaction.
addMessageincrementslast_seqwith an atomic update, then reads that value while InnoDB holds the conversation row lock until commit. It never usesMAX(seq) + 1.- Invite use checks and increments in one conditional update. Mentions replace inside one transaction. Reactions and participant inserts use unique-key idempotent upserts. Active ban creation locks the indexed user range in one transaction.
All adapter results convert database timestamps to real Date instances.