Skip to content
All guides
GuideBeginnersupabasecursordrizzlequickstart

Set Up Supabase with Cursor in 5 Minutes

An opinionated quickstart: Supabase project, Drizzle for type-safe queries, Cursor rules file, your first table. The same template we use to start every new vibe-coded project in 2026.

5 min to complete 8 min read

This is the opinionated setup we use to start every new vibe-coded project. Five minutes of work; the rest of the project is downstream.

Step 1: Create the Supabase project

Head to supabase.com/dashboard, create a new project. Pick the closest region. Save the database password somewhere safe.

From the project settings, grab:

  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • The direct connection string (Settings → Database → Connection string).

Step 2: Bootstrap a Next.js app with Drizzle

terminalbash
pnpm create next-app@latest my-app --typescript --app --tailwind
cd my-app

# Drizzle + postgres-js + Zod
pnpm add drizzle-orm postgres zod
pnpm add -D drizzle-kit @types/pg

Add drizzle.config.ts at the root:

drizzle.config.tsts
import type { Config } from "drizzle-kit";

export default {
  schema: "./src/db/schema.ts",
  out: "./drizzle",
  dialect: "postgresql",
  dbCredentials: { url: process.env.DATABASE_URL! },
} satisfies Config;

Step 3: Wire up the Supabase connection

src/db/client.tsts
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";

const sql = postgres(process.env.DATABASE_URL!, {
  max: 1,           // serverless-friendly
  prepare: false,   // safe for transaction-mode pooling
});

export const db = drizzle(sql);

Add .env.local:

.env.local
DATABASE_URL=postgres://postgres:[password]@db.[project].supabase.co:6543/postgres
NEXT_PUBLIC_SUPABASE_URL=https://[project].supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=ey...

Step 4: Define your first schema

src/db/schema.tsts
import { pgTable, uuid, text, timestamp } from "drizzle-orm/pg-core";

export const posts = pgTable("posts", {
  id:        uuid("id").primaryKey().defaultRandom(),
  authorId:  uuid("author_id").notNull(),
  title:     text("title").notNull(),
  content:   text("content"),
  status:    text("status").$type<"draft" | "published">().notNull().default("draft"),
  createdAt: timestamp("created_at").defaultNow().notNull(),
});

Generate and apply the migration:

terminalbash
pnpm drizzle-kit generate
pnpm drizzle-kit migrate

Step 5: Add a Cursor rules file

Drop .cursorrules (or AGENTS.md if you prefer the open standard) at the repo root:

.cursorrules
When working on database code:

1. Every schema change is a migration in `drizzle/`. Run
   `pnpm drizzle-kit generate` after every `src/db/schema.ts`
   change, then `pnpm drizzle-kit migrate`. Never ALTER TABLE
   in production directly.

2. Read `src/db/schema.ts` before writing any query. Use the
   types from `drizzle-orm` and the inferred row types. Do not
   invent column names.

3. Row-Level Security stays on. Every new table needs a policy in
   the migration that creates the table.

4. Default to the anon Supabase client. Use service_role only with
   an explicit comment explaining why.

Step 6: Ship the first feature

Open Cursor and ask: "create a route at /api/posts that lists the 10 most recent published posts". The agent reads your schema, generates a typed query, returns the route. You ship.