Local Development Guide for the Next.js Supabase Starter Kit
Set up your development environment, understand Makerkit's architecture patterns, and navigate the development guides.
Start local development by running pnpm dev to launch the Next.js app and Supabase services. Makerkit uses a security-first, account-centric architecture where all business data belongs to accounts (personal or team), protected by Row Level Security (RLS) policies enforced at the database level.
Development Setup
Get started with local development
Development Environment
Starting Services
# Start all services (Next.js app + Supabase)pnpm dev# Or start individuallypnpm --filter web dev # Next.js app (port 3000)pnpm run supabase:web:start # Local SupabaseKey URLs
| Service | URL | Purpose |
|---|---|---|
| Main app | http://localhost:3000 | Your application |
| Supabase Studio | http://localhost:54323 | Database admin UI |
| Inbucket (email) | http://localhost:54324 | Local email testing |
Common Commands
# Databasepnpm run supabase:web:reset # Reset database to clean statepnpm --filter web supabase:typegen # Regenerate TypeScript types# Developmentpnpm typecheck # Type check all packagespnpm lint:fix # Fix linting issuespnpm format:fix # Format codeDevelopment Philosophy
Makerkit is built around three core principles that guide all development decisions:
Security by Default
Every feature leverages Row Level Security (RLS) and the permission system. Access controls are built into the database layer, not application code. When you add a new table, you also add RLS policies that enforce who can read, write, and delete data.
Multi-Tenant from Day One
All business data belongs to accounts (personal or team). This design enables both B2C and B2B use cases while ensuring proper data isolation. Every table that holds user-generated data includes an account_id foreign key.
Type-Safe Development
TypeScript types are auto-generated from your database schema. When you modify the database, run pnpm --filter web supabase:typegen to update types. This ensures end-to-end type safety from database to UI.
Development Guides Overview
Database & Data Layer
Start here to understand the foundation:
| Guide | Description |
|---|---|
| Database Architecture | Multi-tenant data model, security patterns, core tables |
| Database Schema | Add tables, RLS policies, triggers, and relationships |
| Migrations | Create and apply schema changes |
| Database Functions | Built-in functions for permissions, roles, subscriptions |
| Database Tests | Test RLS policies with pgTAP |
| Database Webhooks | React to database changes |
Application Development
| Guide | Description |
|---|---|
| Loading Data | Fetch data in Server Components and Client Components |
| Writing Data | Server Actions, forms, and mutations |
| Permissions and Roles | RBAC implementation and permission checks |
Frontend & Marketing
| Guide | Description |
|---|---|
| Marketing Pages | Landing pages, pricing, FAQ |
| Legal Pages | Privacy policy, terms of service |
| SEO | Metadata, sitemap, structured data |
| External Marketing Website | Redirect to Framer, Webflow, etc. |
Architecture & Testing
| Guide | Description |
|---|---|
| Application Tests (E2E) | Playwright E2E testing patterns |
| Adding Turborepo Apps | Add new applications to the monorepo |
| Adding Turborepo Packages | Create shared packages |
Common Development Patterns
The Account-Centric Pattern
Every business entity references an account_id:
create table public.projects ( id uuid primary key default gen_random_uuid(), account_id uuid not null references public.accounts(id) on delete cascade, name text not null, created_at timestamptz not null default now());The Security-First Pattern
Every table has RLS enabled with explicit policies:
alter table public.projects enable row level security;create policy "Members can view their projects" on public.projects for select to authenticated using (public.has_role_on_account(account_id));create policy "Users with write permission can create projects" on public.projects for insert to authenticated with check (public.has_permission(auth.uid(), account_id, 'projects.write'::app_permissions));The Type-Safe Pattern
Database types are auto-generated:
import type { Database } from '@kit/supabase/database';type Project = Database['public']['Tables']['projects']['Row'];type NewProject = Database['public']['Tables']['projects']['Insert'];The Server Action Pattern
Use enhanceAction for validated, authenticated server actions:
import { enhanceAction } from '@kit/next/actions';import { z } from 'zod';const schema = z.object({ name: z.string().min(1), accountId: z.string().uuid(),});export const createProject = enhanceAction( async (data, user) => { // data is validated, user is authenticated const supabase = getSupabaseServerClient(); const { data: project } = await supabase .from('projects') .insert({ name: data.name, account_id: data.accountId }) .select() .single(); return project; }, { schema },);Recommended Learning Path
1. Foundation (Start Here)
- Database Architecture - Understand the multi-tenant model
- Permissions and Roles - Learn RBAC implementation
- Database Schema - Build your first feature
2. Core Development
- Loading Data - Data fetching patterns
- Writing Data - Forms and mutations
- Migrations - Schema change workflow
3. Advanced (As Needed)
- Database Functions - Custom database logic
- Database Webhooks - Event-driven features
- Database Tests - Test RLS policies
Next Steps
- Read Database Architecture to understand the foundation
- Plan your first feature - define entities, relationships, and access rules
- Implement step-by-step following the Database Schema guide
- Test your RLS policies using Database Tests
The guides are designed to be practical and production-ready. Each builds on knowledge from previous ones, developing your expertise with Makerkit's architecture and patterns.