Skip to content

Drizzle ORM with PlanetScale

Use the portable Postgres or MySQL client with PlanetScale. This page owns database configuration; Add Drizzle ORM owns Worker deployment.

drizzle.config.ts
import { defineConfig } from "drizzle-kit";
export default defineConfig({
schema: "./src/schema.ts",
out: "./migrations",
dialect: "postgresql",
});

Use the Postgres schema example with pg-core columns, or select dialect: "mysql" with the MySQL schema.

Terminal window
bunx drizzle-kit generate
git diff -- src/schema.ts migrations
git status --short
git add src/schema.ts drizzle.config.ts migrations
git diff --cached
git commit -m "Add database migration"

Generate when the schema changes, then review and commit the schema, SQL, and snapshots together before deploying. The optional Drizzle.Schema resource is not required for this workflow.

src/Db.ts
import * as Planetscale from "alchemy/Planetscale";
import * as Effect from "effect/Effect";
export const PlanetscaleDb = Effect.gen(function* () {
const database = yield* Planetscale.PostgresDatabase("app-db", {
region: { slug: "us-east" },
clusterSize: "PS_10",
});
const branch = yield* Planetscale.PostgresBranch("app-branch", {
database,
migrations: "./migrations",
});
const role = yield* Planetscale.PostgresRole("app-role", {
database,
branch,
inheritedRoles: ["postgres"],
});
return { database, branch, role };
});

Register Planetscale.providers() in your Stack; the branch applies committed migrations transactionally and the role supplies application credentials. Postgres covers branch and role options.

import * as Cloudflare from "alchemy/Cloudflare";
export const Hyperdrive = Effect.gen(function* () {
const { role } = yield* PlanetscaleDb;
return yield* Cloudflare.Hyperdrive.Connection("app-hyperdrive", {
origin: role.origin,
dev: role.pooledOrigin,
caching: { disabled: true },
});
});

role.origin uses direct port 5432, while role.pooledOrigin uses PSBouncer on port 6432. Other runtimes can consume the redacted connectionUrl or connectionUrlPooled; the Cloudflare walkthrough continues from this Hyperdrive resource.

drizzle.config.ts
dialect: "postgresql",
dialect: "mysql",

For Vitess, use mysql-core columns and run the same generate, review, and commit commands above. Do not reuse Postgres migration SQL on a MySQL branch.

src/Db.ts
import * as Planetscale from "alchemy/Planetscale";
import * as Effect from "effect/Effect";
export const PlanetscaleDb = Effect.gen(function* () {
const database = yield* Planetscale.MySQLDatabase("app-db", {
region: { slug: "us-east" },
clusterSize: "PS_10",
});
const branch = yield* Planetscale.MySQLBranch("app-branch", {
database,
isProduction: false,
migrations: "./migrations",
});
const password = yield* Planetscale.MySQLPassword("app-password", {
database,
branch,
role: "readwriter",
});
return { database, branch, password };
});

MySQLPassword provides password.origin for Hyperdrive, without a pooled variant. PlanetScale migrations covers statement splitting and MySQL’s non-transactional DDL behavior.

Terminal window
bun alchemy deploy

The branch applies pending committed files; this configuration does not generate SQL during deployment. If drizzle-kit migrate already owns application, keep that workflow unless you deliberately choose Alchemy-managed migrations; see migration ownership.

Follow Add Drizzle ORM for Worker bindings, or SQL databases to choose a client for another runtime. Existing Postgres and MySQL projects demonstrate connections and queries, but use the optional schema resource rather than this guide’s generation workflow.

Read Preview branches per PR for branch provisioning, and Postgres or MySQL for database-specific behavior.