CrabStack
A full-stack starter kit for indie hackers and small teams. A NestJS backend (PostgreSQL), a Next.js 16 frontend, and a single REST + OpenAPI contract that ties them together. Auth, Stripe billing, blog, email, admin tools, and an MCP server for AI agents are all wired up.
Tech Stack
Backend (port 4000)
- NestJS 11 with TypeScript 6
- PostgreSQL via Knex.js + pg
- REST API with OpenAPI 3.x via
@nestjs/swagger+nestjs-zod - Zod 4 schemas everywhere (validation + DTO + spec emission, single source)
- Cron-based queue worker (
@nestjs/schedule) for bulk email + GDPR ghost-deletion - MCP server (
@modelcontextprotocol/sdk) for AI-agent admin operations
Frontend (port 3000)
- Next.js 16 (App Router) with React 19
- NextAuth v5 (Auth.js, beta) with database session strategy and a custom REST adapter
openapi-fetch0.13 +openapi-typescript7: typed client generated from the shared OpenAPI spec, no method-spaghetti codegen- Server Components + Server Actions for all backend reads/writes (bearer never touches client JS)
- Magic-link sign-in via NextAuth Nodemailer provider (synchronous send)
- SASS modules (no UI library: pick per app)
- Server Actions + React 19
useActionStatefor forms, native validation
What's Included
- Auth: OAuth (GitHub, Google) + magic-link sign-in, database sessions, custom NextAuth REST adapter (14 endpoints under
/internal/auth-adapter/*). See Auth Flow. - Items: Example CRUD module with ownership checks, used as the reference shape for new features. Demos the billing gate (free plan caps at 100 items).
- Stripe billing: Checkout + Customer Portal, idempotent webhook state machine,
getEntitlement()for gating features. Optional by config. See Billing. - Blog engine: Markdown content, SEO-friendly slugs, admin CRUD, tags, reading time, view counts.
- Email system: Magic-link emails sent synchronously by the frontend. Bulk and transactional emails go through a backend queue using
FOR UPDATE SKIP LOCKEDfor native job claiming. See Email. - Admin tools: Protected admin endpoints under
/admin/*plus a backoffice UI gated at the edge (frontend/proxy.ts). - Admin MCP server: AI agent interface at
POST /admin/mcp. See Admin MCP Server. - GDPR account deletion: Queue-based ghost deletion inside a single ACID transaction.
- SEO: Sitemap, robots.txt, OpenGraph, Twitter cards, ISR on blog routes.
- Seed data + dev tools: MailDev for local SMTP, OpenAPI type generation, seed script.
- Docker Compose: One command to run everything.
- CI: Lint + tests + build on the backend and the frontend, plus an OpenAPI drift check that fails if the committed
openapi.jsondrifts from what the backend emits.
Quick Start
Option 1: Docker
docker compose up frontend backend # API + UI
docker compose up frontend backend maildev # + magic-link emails
Frontend at http://localhost:3000, backend at http://localhost:4000, MailDev at http://localhost:1080.
Option 2: Local
Make sure Postgres is running locally and a crabstack database exists.
cd backend
yarn install
yarn setup # generates .env in backend/ and frontend/ with fresh secrets
yarn migrate # create tables
yarn dev # http://localhost:4000
# Separate terminal
cd frontend
yarn install && yarn dev # http://localhost:3000
yarn setup writes random AUTH_SECRET, INTERNAL_API_TOKEN, and ADMIN_MCP_API_KEY into both .env files, using the same value for the shared ones (AUTH_SECRET, INTERNAL_API_TOKEN) so the two sides match. It refuses to overwrite an existing .env, so it's safe to re-run. Rotate before production.
Manual alternative. If you'd rather wire the files by hand, cp .env.example .env in each of backend/ and frontend/, then replace every generate-* placeholder with a real value (openssl rand -base64 32). Keep AUTH_SECRET and INTERNAL_API_TOKEN identical across both files.
For magic-link sign-in locally, run MailDev in the backend directory:
yarn dev:email # SMTP on :1025, web UI on http://localhost:1080
The default EMAIL_SERVER_* values in frontend/.env.example already point at MailDev.
OpenAPI spec at http://localhost:4000/openapi.json. Swagger UI at http://localhost:4000/docs (development mode only: NODE_ENV === 'development').
Seed data
cd backend && yarn seed
Populates sample users, items, blog posts, and email campaigns.
Project Structure
crabstack/
backend/ # NestJS backend (PostgreSQL via Knex)
src/
auth/ # SessionGuard, AdminGuard, InternalApiTokenGuard
items/ # Example CRUD module
blog/ # Public + admin blog
admin/ # /admin/users
email/ # Worker, admin dashboard
internal/ # /internal/auth-adapter/* (NextAuth adapter)
mcp-admin/ # AI-agent admin interface
db/types.ts # Knex table types
migrations/ # Knex migrations
frontend/ # Next.js 16 (App Router)
app/ # RSC pages + colocated Server Actions (`actions.ts`)
lib/
api.ts # api / serverApi() / internalApi() openapi-fetch clients
auth-adapter.ts # BackendAdapter(): calls /internal/auth-adapter/*
types/api.d.ts # Auto-generated by `yarn generate-types`
openapi.json # Canonical contract emitted by the backend
docs/ # You are here
Documentation
- Architecture - How the pieces fit together
- Auth Flow - Sign-in flows, session validation, RSC + Server Actions
- Adding a Module - Step-by-step REST + zod walkthrough
- Admin MCP Server - AI agent interface
- Email - Magic-link send + backend queue
- Billing - Stripe checkout, webhooks, entitlements
- Environment Variables - Full reference for all projects