
Supabase Nextjs
Scaffold Next.js App Router apps with Supabase Auth and Drizzle ORM using server-first patterns and a clear folder layout.
Overview
supabase-nextjs is an agent skill for the Build phase that applies Next.js App Router patterns with Supabase Auth and Drizzle ORM.
Install
npx skills add https://github.com/alinaqi/claude-bootstrap --skill supabase-nextjsWhat is this skill?
- Core principle: Drizzle for queries, Supabase for auth/storage/realtime, server components by default
- Documents App Router layout: `(auth)`, `(dashboard)`, `api` routes, and `callback` auth handler
- Separates `lib/supabase` browser, server, and middleware clients
- Places Drizzle client, schema, and query modules under `src/db/`
- References official Supabase Next.js and Drizzle integration guides
Adoption & trust: 570 installs on skills.sh; 691 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are wiring Next.js and Supabase together and keep blurring server auth, client hooks, and database access responsibilities.
Who is it for?
Indie SaaS builders starting or refactoring a Next.js + Supabase stack who want Drizzle instead of raw Supabase queries everywhere.
Skip if: Non-Next frameworks, Supabase-only quickstarts without Drizzle, or teams that will not touch `src/db` schema work.
When should I use this skill?
When building a Next.js app with Supabase backend (paths: `src/app/**`, `src/db/**`, `supabase/**`).
What do I get? / Deliverables
You implement a consistent App Router structure with Drizzle queries, Supabase auth clients, and server-first components aligned to Supabase docs.
- Auth route group and callback handler layout
- Drizzle schema and query module structure under `src/db/`
- Supabase browser/server/middleware client separation
Recommended Skills
Journey fit
How it compares
Opinionated Next.js + Drizzle + Supabase layout skill, not a generic Postgres or Vercel deploy skill.
Common Questions / FAQ
Who is supabase-nextjs for?
Solo builders using Claude Bootstrap skills while implementing Next.js apps with Supabase backends and Drizzle ORM.
When should I use supabase-nextjs?
During Build backend work when editing `src/app`, `src/db`, or `supabase` paths for auth, queries, and API routes on a Next.js + Supabase project.
Is supabase-nextjs safe to install?
It guides code structure only; review the Security Audits panel on this page and never commit Supabase service role keys to the repo.
SKILL.md
READMESKILL.md - Supabase Nextjs
# Supabase + Next.js Skill Next.js App Router patterns with Supabase Auth and Drizzle ORM. **Sources:** [Supabase Next.js Guide](https://supabase.com/docs/guides/auth/server-side/nextjs) | [Drizzle + Supabase](https://supabase.com/docs/guides/database/drizzle) --- ## Core Principle **Drizzle for queries, Supabase for auth/storage, server components by default.** Use Drizzle ORM for type-safe database access. Use Supabase client for auth, storage, and realtime. Prefer server components; use client components only when needed. --- ## Project Structure ``` project/ ├── src/ │ ├── app/ │ │ ├── (auth)/ │ │ │ ├── login/page.tsx │ │ │ ├── signup/page.tsx │ │ │ └── callback/route.ts │ │ ├── (dashboard)/ │ │ │ └── page.tsx │ │ ├── api/ │ │ │ └── [...]/route.ts │ │ ├── layout.tsx │ │ └── page.tsx │ ├── components/ │ │ ├── auth/ │ │ └── ui/ │ ├── db/ │ │ ├── index.ts # Drizzle client │ │ ├── schema.ts # Schema definitions │ │ └── queries/ # Query functions │ ├── lib/ │ │ ├── supabase/ │ │ │ ├── client.ts # Browser client │ │ │ ├── server.ts # Server client │ │ │ └── middleware.ts # Auth middleware helper │ │ └── auth.ts # Auth helpers │ └── middleware.ts # Next.js middleware ├── supabase/ │ ├── migrations/ │ └── config.toml ├── drizzle.config.ts └── .env.local ``` --- ## Setup ### Install Dependencies ```bash npm install @supabase/supabase-js @supabase/ssr drizzle-orm postgres npm install -D drizzle-kit ``` ### Environment Variables ```bash # .env.local NEXT_PUBLIC_SUPABASE_URL=http://localhost:54321 NEXT_PUBLIC_SUPABASE_ANON_KEY=<from supabase start> # Server-side only SUPABASE_SERVICE_ROLE_KEY=<from supabase start> DATABASE_URL=postgresql://postgres:postgres@localhost:54322/postgres ``` --- ## Drizzle Setup ### drizzle.config.ts ```typescript import { defineConfig } from 'drizzle-kit'; export default defineConfig({ schema: './src/db/schema.ts', out: './supabase/migrations', dialect: 'postgresql', dbCredentials: { url: process.env.DATABASE_URL!, }, schemaFilter: ['public'], }); ``` ### src/db/index.ts ```typescript import { drizzle } from 'drizzle-orm/postgres-js'; import postgres from 'postgres'; import * as schema from './schema'; const client = postgres(process.env.DATABASE_URL!, { prepare: false, // Required for Supabase connection pooling }); export const db = drizzle(client, { schema }); ``` ### src/db/schema.ts ```typescript import { pgTable, uuid, text, timestamp, boolean, } from 'drizzle-orm/pg-core'; export const profiles = pgTable('profiles', { id: uuid('id').primaryKey(), // References auth.users email: text('email').notNull(), name: text('name'), avatarUrl: text('avatar_url'), createdAt: timestamp('created_at').defaultNow().notNull(), updatedAt: timestamp('updated_at').defaultNow().notNull(), }); export const posts = pgTable('posts', { id: uuid('id').primaryKey().defaultRandom(), authorId: uuid('author_id').references(() => profiles.id).notNull(), title: text('title').notNull(), content: text('content'), published: boolean('published').default(false), createdAt: timestamp('created_at').defaultNow().notNull(), }); // Type exports export type Profile = typeof profiles.$inferSelect; export type NewProfile = typeof profiles.$inferInsert; export type Post = typeof posts.$inferSelect; export type NewPost = typeof posts.$inferInsert; ``` --- ## Supabase Clients ### src/lib/supabase/client.ts (Browser) ```typescript import { createBrowserClient } from '@supabase/ssr'; export function createClient() { return createBrowserClient(