PlanetScale
PlanetScale is a cloud database platform built for speed and scalability. This guide covers how to set it up with SST.
Install
Add the PlanetScale provider to your SST app. Learn more about providers.
sst add planetscaleThis adds the provider to your sst.config.ts.
{ providers: { planetscale: "1.0.0", },}Then set the PLANETSCALE_SERVICE_TOKEN and PLANETSCALE_SERVICE_TOKEN_ID environment variables. You can create a service token in the PlanetScale dashboard under Settings > Service tokens. The token needs at least the following permissions for the upcoming examples:
connect_branchconnect_production_branchcreate_branchdelete_branchdelete_branch_passwordread_branchread_database
Reference a database
Reference an existing PlanetScale Vitess database directly from your SST config.
const db = planetscale.getDatabaseVitessOutput({ id: "mydb", organization: "myorg",});Branch per stage
PlanetScale supports database branching natively. Combined with SST, every stage and every pull request can get its own isolated database branch automatically.
Conditionally create or reference a branch based on the current stage.
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, });In production, it references the existing default branch. In any other stage, it creates a new branch from it. So sst deploy --stage pr-42 creates a pr-42 branch in PlanetScale.
This is a pattern used in production by terminal.shop and OpenCode.
Postgres role successors
When you remove a stage whose resources have their removal policy set to "remove", SST deletes its Postgres branch roles. A role that owns database objects, because your app or a migration tool created them, can’t be deleted unless PlanetScale knows which role should take those objects over. Without one, the delete fails with a 422.
Set successor to the role that should receive them. Here db and branch are the Postgres ones, from getDatabasePostgresOutput and a PostgresBranch or getPostgresBranchOutput, in place of the Vitess ones above.
const role = new planetscale.PostgresBranchRole("DatabaseRole", { database: db.name, organization: db.organization, branch: branch.name, name: `${$app.name}-${$app.stage}`, inheritedRoles: [ "pg_read_all_data", "pg_write_all_data", "postgres", // Only needed for pushing schema changes ], successor: "postgres",});Set it on every role you create, including runtime-only roles that own nothing yet, since they may create or own objects later.
Link to your app
Create a password for the branch and wrap it in a sst.Linkable.
const password = new planetscale.VitessBranchPassword("DatabasePassword", { database: db.name, organization: db.organization, branch: branch.name, role: "admin", name: `${$app.name}-${$app.stage}`,});
export const database = new sst.Linkable("Database", { properties: { host: password.accessHostUrl, username: password.username, password: password.plainText, database: db.name, port: 3306, },});The Linkable component lets you wrap arbitrary values and make them available to any function or service you link it to. Learn more about linking resources.
Connect to the database
Link the database to any function or service.
new sst.aws.Function("Api", { handler: "src/api.handler", link: [database],});Then access the credentials in a type-safe way through Resource. For example, with Drizzle ORM.
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, },});You can use any other ORM or database driver the same way.
Examples
Check out the full examples: