AWS PlanetScale Drizzle MySQL
In this example, we use PlanetScale with a branch-per-stage pattern. Every stage gets its own database branch — so each PR can have an isolated database.
const db = planetscale.getDatabaseVitessOutput({ id: "mydb", organization: "myorg",});
const branch = $app.stage === "production" ? planetscale.getVitessBranchOutput({ id: db.defaultBranch, organization: db.organization, database: db.name, }) : new planetscale.VitessBranch("DatabaseBranch", { database: db.name, organization: db.organization, name: $app.stage, parentBranch: db.defaultBranch, });We then create a password and wrap it in a Linkable to link it to a function.
new sst.aws.Function("Api", { handler: "src/api.handler", link: [database], url: true,});You can push your Drizzle schema changes to PlanetScale with:
bun run db:pushIn the function we use Drizzle ORM with the
Resource helper.
import { drizzle } from "drizzle-orm/planetscale-serverless";import { Resource } from "sst";
export const db = drizzle({ connection: { host: Resource.Database.host, username: Resource.Database.username, password: Resource.Database.password, },});const db = planetscale.getDatabaseVitessOutput({ id: "example", organization: "vimtor",});
const branch = $app.stage === "production" ? planetscale.getVitessBranchOutput({ id: db.defaultBranch, organization: db.organization, database: db.name, }) : new planetscale.VitessBranch("DatabaseBranch", { database: db.name, organization: db.organization, name: $app.stage, parentBranch: db.defaultBranch, });
const password = new planetscale.VitessBranchPassword("DatabasePassword", { database: db.name, organization: db.organization, branch: branch.name, role: "admin", name: `${$app.name}-${$app.stage}`,});
const database = new sst.Linkable("Database", { properties: { host: password.accessHostUrl, username: password.username, password: password.plainText, database: db.name, port: 3306, },});
const api = new sst.aws.Function("Api", { handler: "src/api.handler", link: [database], url: true,});import { db } from "./drizzle";import { todosTable } from "./schema";import { APIGatewayProxyHandlerV2 } from "aws-lambda";
export const handler: APIGatewayProxyHandlerV2 = async (evt) => { if (evt.requestContext.http.method === "GET") { const result = await db.select().from(todosTable).execute(); return { statusCode: 200, body: JSON.stringify(result), }; }
if (evt.requestContext.http.method === "POST") { await db.insert(todosTable).values({ title: "new todosTable" }).execute(); return { statusCode: 200, body: "created", }; }
return { statusCode: 404, body: "not found", };};View the full example.