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 Environment

Starting Services

# Start all services (Next.js app + Supabase)
pnpm dev
# Or start individually
pnpm --filter web dev # Next.js app (port 3000)
pnpm run supabase:web:start # Local Supabase

Key URLs

ServiceURLPurpose
Main apphttp://localhost:3000Your application
Supabase Studiohttp://localhost:54323Database admin UI
Inbucket (email)http://localhost:54324Local email testing

Common Commands

# Database
pnpm run supabase:web:reset # Reset database to clean state
pnpm --filter web supabase:typegen # Regenerate TypeScript types
# Development
pnpm typecheck # Type check all packages
pnpm lint:fix # Fix linting issues
pnpm format:fix # Format code

Development 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:

GuideDescription
Database ArchitectureMulti-tenant data model, security patterns, core tables
Database SchemaAdd tables, RLS policies, triggers, and relationships
MigrationsCreate and apply schema changes
Database FunctionsBuilt-in functions for permissions, roles, subscriptions
Database TestsTest RLS policies with pgTAP
Database WebhooksReact to database changes

Application Development

GuideDescription
Loading DataFetch data in Server Components and Client Components
Writing DataServer Actions, forms, and mutations
Permissions and RolesRBAC implementation and permission checks

Frontend & Marketing

GuideDescription
Marketing PagesLanding pages, pricing, FAQ
Legal PagesPrivacy policy, terms of service
SEOMetadata, sitemap, structured data
External Marketing WebsiteRedirect to Framer, Webflow, etc.

Architecture & Testing

GuideDescription
Application Tests (E2E)Playwright E2E testing patterns
Adding Turborepo AppsAdd new applications to the monorepo
Adding Turborepo PackagesCreate 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 },
);

1. Foundation (Start Here)

  1. Database Architecture - Understand the multi-tenant model
  2. Permissions and Roles - Learn RBAC implementation
  3. Database Schema - Build your first feature

2. Core Development

  1. Loading Data - Data fetching patterns
  2. Writing Data - Forms and mutations
  3. Migrations - Schema change workflow

3. Advanced (As Needed)

Next Steps

  1. Read Database Architecture to understand the foundation
  2. Plan your first feature - define entities, relationships, and access rules
  3. Implement step-by-step following the Database Schema guide
  4. 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.