Architecture and Technologies of Makerkit Next.js Prisma
Understand the technologies, architecture, and patterns used in Makerkit. This foundation will help you navigate the codebase and build features effectively.
This module covers the technologies, architecture, and patterns that power Makerkit. Understanding these foundations will help you navigate the codebase and build features effectively throughout the course.
What is Makerkit?
Makerkit is a SaaS starter kit that provides the foundational features every SaaS application needs:
- Authentication - Sign up, sign in, password reset, OAuth, MFA
- Multi-tenancy - Organizations, team members, invitations
- Billing - Subscriptions, checkout, customer portal
- Admin Dashboard - User management, metrics, impersonation
- Email - Transactional emails with templates
- Settings - User profile, organization settings, preferences
Instead of building these from scratch, you start with a working foundation and customize for your specific product.
B2B vs B2C Modes
Makerkit supports two account modes:
| Mode | Description | Use Case |
|---|---|---|
| B2B (organizations-only) | Users belong to organizations | Team collaboration tools, business software |
| B2C (personal-accounts-only) | Users have individual accounts | Consumer apps, personal tools |
| Hybrid | Users can have both | Platforms with personal and team features |
In this course, we use B2B mode for TeamPulse - a team feedback tool.
Core Technologies
The Stack
┌─────────────────────────────────────────────────────────┐│ Frontend ││ Next.js 16 + React 19 + TypeScript + Tailwind CSS 4 │├─────────────────────────────────────────────────────────┤│ UI Components ││ shadcn/ui + Base UI + Lucide Icons │├─────────────────────────────────────────────────────────┤│ Authentication + Subscriptions + Multi-tenancy ││ Better Auth │├─────────────────────────────────────────────────────────┤│ Database ││ Prisma ORM + PostgreSQL │├─────────────────────────────────────────────────────────┤│ Payments ││ Stripe │└─────────────────────────────────────────────────────────┘Next.js 16 with App Router
Next.js is the React framework that powers the application:
- App Router - File-based routing with layouts and loading states
- Server Components - Fetch data on the server, reduce client JavaScript
- Server Actions - Call server functions directly from components
- Streaming - Progressive rendering with Suspense
React 19
The latest React version with:
- Server Components - Default for new components
- Server Actions - Form handling with automatic pending states
TypeScript
Full type safety from database to UI:
- Type-safe database queries with Prisma
- Type-safe API with Zod validation
- Type-safe forms with react-hook-form
Tailwind CSS 4 + shadcn/ui
Styling approach:
- Tailwind CSS 4 - Utility-first CSS framework
- shadcn/ui - Pre-built accessible components
- Base UI - Headless component primitives
- Lucide Icons - Icon library
Better Auth
Authentication framework that handles:
- Email/password authentication
- OAuth providers (Google, GitHub, etc.)
- Multi-factor authentication (TOTP)
- Session management
- Organizations and teams
- Role-based access control
- Subscriptions and billing
Prisma ORM
Type-safe database toolkit:
- Schema defined in Prisma schema language (
packages/database/src/prisma/schema.prisma) - SQL-like query builder
- Automatic migrations
- Direct PostgreSQL access
PostgreSQL
The default relational database. Makerkit uses PostgreSQL for all data storage, with Prisma providing type-safe access.
Stripe
The default payment processor. Handles subscriptions, checkout, and customer portal functionality.
Turborepo & Monorepo Structure
Makerkit uses a monorepo - multiple packages in one repository, managed by Turborepo.
Why a Monorepo?
- Code Sharing - Reuse packages across apps
- Consistent Tooling - Same linting, formatting, TypeScript config
- Atomic Changes - Update related code together
- Simplified Dependencies - One
pnpm installfor everything
How Turborepo Works
Turborepo orchestrates tasks across packages:
pnpm dev # Starts all apps in developmentpnpm typecheck # Type-checks all packagespnpm lint:fix # Lints all packagespnpm format:fix # Formats all codeKey features:
- Task Caching - Skips unchanged packages
- Parallel Execution - Runs independent tasks concurrently
- Dependency Graph - Builds packages in correct order
Package Structure
makerkit/├── apps/│ ├── web/ # Main Next.js application│ └── e2e/ # Playwright end-to-end tests│├── packages/│ ├── ui/ # shadcn/ui components│ ├── database/ # Prisma schema & migrations│ ├── better-auth/ # Auth configuration & plugins│ ├── auth/ # Auth utilities & middleware│ ├── billing/ # Payment provider abstraction│ │ ├── api/ # Billing API endpoints│ │ ├── core/ # Core billing logic│ │ ├── hooks/ # Billing React hooks│ │ ├── stripe/ # Stripe integration│ │ └── ui/ # Billing UI components│ ├── email-templates/ # React Email templates│ ├── mailers/ # Email sending (Resend, Nodemailer)│ ├── rbac/ # Role-based access control│ ├── account/ # Account features (hooks, settings, components)│ ├── organization/ # Organization features│ ├── admin/ # Admin dashboard│ ├── i18n/ # Internationalization│ ├── monitoring/ # Error tracking (Sentry)│ ├── analytics/ # Analytics integration│ ├── storage/ # File storage│ ├── otp/ # One-time passwords│ ├── policies/ # Access policies│ ├── cms/ # Content management│ └── shared/ # Shared utilities│├── turbo.json # Turborepo configuration├── pnpm-workspace.yaml # Workspace definition└── package.json # Root dependenciesPackage Dependencies
┌─────────────┐ │ apps/web │ └──────┬──────┘ │ ┌─────────────────┼─────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌──────────┐ ┌──────────┐ │ ui │ │ admin │ │ billing │ └─────────┘ └────┬─────┘ └────┬─────┘ │ │ ▼ ▼ ┌──────────┐ ┌──────────┐ │ auth │ │ database │ └────┬─────┘ └──────────┘ │ ▼ ┌────────────┐ │better-auth │ └────────────┘Application Architecture
Request Flow
┌──────────┐ ┌──────────────┐ ┌─────────────┐ ┌──────────┐│ Browser │────▶│ Next.js │────▶│ Better Auth │────▶│ Database ││ │◀────│ App Router │◀────│ Session │◀────│PostgreSQL│└──────────┘ └──────────────┘ └─────────────┘ └──────────┘- Browser makes request to Next.js route
- Next.js handles routing, renders Server Components
- Better Auth validates session, provides user context
- Database stores and retrieves data via Prisma
Multi-Tenancy Model
┌─────────────────────────────────────────────────────────┐│ User ││ (authentication) │└─────────────────────────┬───────────────────────────────┘ │ │ belongs to (via membership) ▼┌─────────────────────────────────────────────────────────┐│ Organization ││ (tenant boundary) │├─────────────────────────────────────────────────────────┤│ Members │ Invitations │ Subscription │├─────────────────────────────────────────────────────────┤│ Application Data ││ (scoped to organization) │└─────────────────────────────────────────────────────────┘- Users can belong to multiple organizations
- Organizations are the tenant boundary
- All data is scoped to organizations
- Members have roles (owner, admin, member)
Billing Flow
┌──────────────┐ ┌─────────────┐ ┌──────────┐│ Organization │────▶│ Stripe │────▶│ Checkout ││ Billing │ │ Checkout │ │ Page │└──────────────┘ └─────────────┘ └────┬─────┘ │ │ payment ▼┌──────────────┐ ┌─────────────┐ ┌──────────┐│ Database │◀────│ Webhook │◀────│ Stripe ││ Subscription │ │ Handler │ │ Events │└──────────────┘ └─────────────┘ └──────────┘- User initiates checkout from the billing page
- Stripe Checkout handles payment
- Stripe sends webhook events
- Webhook handler updates subscription in database
Better Auth manages webhooks automatically.
Key Patterns
Server Components vs Client Components
// Server Component (default) - renders on the serverexport default async function DashboardPage() { const data = await fetchDashboardData(); return <Dashboard data={data} />;}// Client Component - renders on server, hydrates on client'use client';export function Counter() { const [count, setCount] = useState(0); return <button onClick={() => setCount(c => c + 1)}>{count}</button>;}Use Server Components for:
- Data fetching
- Accessing backend resources
- Keeping sensitive logic server-side
Use Client Components for:
- Interactivity (onClick, onChange)
- Browser APIs
- React hooks (useState, useEffect)
Server Actions with next-safe-action
// Define action with validationexport const updateProfileAction = authenticatedActionClient .inputSchema(UpdateProfileSchema) .action(async ({ parsedInput, ctx }) => { const { user } = ctx; await db.user.update({ where: { id: user.id }, data: { name: parsedInput.name }, }); revalidatePath('/settings'); return { success: true }; });// Use in componentconst { execute, status } = useAction(updateProfileAction);const isPending = status === 'executing';Data Loading Pattern
React's cache function deduplicates data fetches on a per-request basis. If multiple components call the same loader, the data is fetched once and shared.
// Loader function (cached, server-side)export const loadDashboardData = cache(async () => { const orgId = await requireActiveOrganizationId(); return db.board.findMany({ where: { organizationId: orgId }, });});// Page componentexport default async function DashboardPage() { const data = await loadDashboardData(); return <DashboardView data={data} />;}Feature Overview
Authentication
| Feature | Implementation |
|---|---|
| Email/Password | Better Auth credential provider |
| OAuth | Google, GitHub, etc. via Better Auth |
| Magic Links | Passwordless sign-in |
| MFA/2FA | TOTP with authenticator apps |
| Password Reset | Email-based recovery |
| Email Verification | Required before access |
Organizations & Teams
| Feature | Implementation |
|---|---|
| Create Organization | Auto on signup or manual |
| Invite Members | Email invitations with role |
| Role Hierarchy | Owner > Admin > Member |
| Remove Members | With permission checks |
| Transfer Ownership | Owner can transfer |
Billing & Subscriptions
| Feature | Implementation |
|---|---|
| Checkout | Stripe Checkout sessions |
| Subscriptions | Monthly/yearly plans |
| Plan Limits | Seat-based or feature-based |
| Customer Portal | Manage payment methods |
| Webhooks | Automatic status updates |
Admin Dashboard
| Feature | Implementation |
|---|---|
| User Management | List, search, filter users |
| Ban/Unban | Block user access |
| Impersonation | Act as user temporarily |
| Metrics | User counts, session stats |
| Organization View | Browse all organizations |
Project Files Overview
Key Directories
| Path | Purpose |
|---|---|
apps/web/app/ | Next.js routes and pages |
apps/web/config/ | Application configuration |
apps/web/lib/ | App-specific utilities |
packages/ui/src/ | Shared UI components |
packages/database/src/ | Database schema |
Important Files
| File | Purpose |
|---|---|
apps/web/.env.local | Environment variables (secrets) |
apps/web/config/app.config.ts | App settings validation |
apps/web/config/auth.config.ts | OAuth providers config |
apps/web/config/account-mode.config.ts | Account mode (B2B/B2C) settings |
apps/web/config/feature-flags.config.ts | Feature toggles |
apps/web/app/[locale]/(internal)/_config/navigation.config.tsx | Sidebar navigation |
packages/database/src/prisma/schema.prisma | Database schema |
Route Structure
apps/web/app/[locale]/├── (public)/ # Public pages│ ├── page.tsx # Home page│ ├── blog/ # Blog pages│ ├── faq/ # FAQ page│ ├── changelog/ # Changelog pages│ ├── password-reset/ # Password reset flow│ └── (legal)/ # Legal pages (privacy, terms)├── auth/ # Auth pages (sign-in, sign-up, verify)├── help/ # Help center├── admin/ # Admin pages (users, organizations)├── (internal)/ # Protected app pages│ ├── dashboard/ # User dashboard│ └── settings/ # Account settings│ ├── billing/ # Subscription management│ ├── members/ # Team members│ ├── organization/ # Organization settings│ ├── preferences/ # User preferences│ └── security/ # Security settings (MFA, sessions)└── (invitation)/ # Invitation acceptanceapps/web/app/api/ # API routes (NOT under [locale])├── auth/[...all]/ # Better Auth endpoints└── version/ # Version endpointEnvironment Variables
Environment variables control application behavior. See apps/web/.env and apps/web/.env.development for defaults.
Public Variables (exposed to browser)
Variables prefixed with NEXT_PUBLIC_ are exposed to the browser. Use them for public information only - never for sensitive data.
NEXT_PUBLIC_SITE_URL=http://localhost:3000NEXT_PUBLIC_ACCOUNT_MODE=organizations-only # or: personal-only, hybridNEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...The NEXT_PUBLIC_ACCOUNT_MODE values control the app mode:
organizations-only- B2B mode, users must belong to an organizationpersonal-only- B2C mode, personal accounts onlyhybrid- Mixed mode, supports both
Private Variables (server-only)
Variables without the NEXT_PUBLIC_ prefix are server-only and inaccessible from the client.
DATABASE_URL=postgresql://...BETTER_AUTH_SECRET=random-32-char-secretSTRIPE_SECRET_KEY=sk_test_...STRIPE_WEBHOOK_SECRET=whsec_...Common Commands
Development
# Start Docker services first (PostgreSQL, Mailpit)pnpm dev:compose:up# Start the web apppnpm dev# Start only the web app (if services already running)pnpm --filter web devNote:
pnpm devstarts the Next.js development server but does not start Docker services. Runpnpm dev:compose:upfirst to start the database and email services.
Code Quality
# Type-check all packagespnpm typecheck# Type-check specific packagepnpm --filter web typecheckpnpm --filter @kit/database typecheck# Lint and auto-fix issuespnpm lint:fix# Format code with Prettierpnpm format:fixDatabase
# Generate Prisma clientpnpm --filter @kit/database prisma:generate# Push schema changes to databasepnpm --filter @kit/database prisma db push# Run migrationspnpm --filter @kit/database prisma:migrate# Open Prisma Studio (database UI)pnpm --filter @kit/database prisma:studio# Seed the database with mock datapnpm seedTesting
# Run E2E testspnpm --filter e2e test# Run E2E tests with UIpnpm --filter e2e test:uiInstalling Dependencies
# Install all dependenciespnpm install# Add dependency to specific packagepnpm --filter web add package-namepnpm --filter @kit/ui add package-nameUseful Shortcuts
| Command | What it does |
|---|---|
pnpm dev | Start everything for development |
pnpm typecheck | Verify TypeScript is happy |
pnpm lint:fix | Fix linting issues automatically |
pnpm format:fix | Format all code consistently |
pnpm build | Build for production |
Development Workflow
# After making changes:pnpm typecheck # Check for TypeScript errorspnpm lint:fix # Fix linting issuespnpm format:fix # Format codepnpm dev # Test locallyModule Complete
You now understand:
- What Makerkit provides out of the box
- The technology stack and why each piece was chosen
- How the monorepo is structured with Turborepo
- The application architecture and data flow
- Key patterns for building features
- Where to find things in the codebase
Next: In Module 1: Setup & Configuration, you'll set up your development environment and configure TeamPulse as a B2B application.