UllrAI SaaS Starter Kit Developer Documentation
Featured
Jun 24, 2025
15 min read
UllrAI

UllrAI SaaS Starter Kit Developer Documentation

This documentation provides a comprehensive and in-depth technical reference for developers using the UllrAI SaaS Starter Kit. Whether you want to quickly launch a new project or perform deep customization and secondary development, this documentation will provide you with the necessary guidance.

Next.jsSaaS StarterTypeScriptTailwind CSSshadcn/uiDrizzle ORMCreemResendCloudflare R2

UllrAI SaaS Starter Kit Developer Documentation

This documentation provides a comprehensive and in-depth technical reference for developers using the UllrAI SaaS Starter Kit. Whether you want to quickly launch a new project or perform deep customization and secondary development, this documentation will provide you with the necessary guidance.

1. Project Overview

1.1. Project Introduction

UllrAI SaaS Starter Kit is a free, open-source, production-ready full-stack SaaS starter kit. It integrates the most respected technologies and practices in modern web development, designed to help developers launch their next SaaS project at unprecedented speed, allowing you to focus on business logic rather than infrastructure setup.

  • Core Features: Provides authentication, payment subscriptions, database management, file uploads, content management, and other core SaaS application features.
  • Technology Stack: Based on Next.js 15 App Router, TypeScript, PostgreSQL, Drizzle ORM, and integrates Creem payments, Resend email service, and Cloudflare R2 file storage.
  • Use Cases:
    • Quickly build full-stack SaaS applications requiring user login and paid subscription features.
    • As a practical project for learning modern full-stack web development technologies.
    • Provide a stable, scalable initial scaffold for enterprise-level projects.
    • Independent developers or small teams quickly validate business ideas.

1.2. Quick Start

1.Clone the project

git clone https://github.com/ullrai/saas-starter.git
cd saas-starter

2.Install dependencies

pnpm install

3.Configure environment Copy .env.example to .env and fill in all required environment variables.

cp .env.example .env

4.Sync database Make sure your local PostgreSQL database is running, then execute:

pnpm db:push

5.Run development server

pnpm dev

The application will run at http://localhost:3000.

1.3. Feature List

  • Modern Framework: Next.js 15 (App Router, RSC), React 19, TypeScript
  • UI: Tailwind CSS v4, shadcn/ui, Lucide Icons, Dark/Light Mode
  • Authentication: Better-Auth (Magic Link, OAuth - Google/GitHub/LinkedIn)
  • Database: PostgreSQL + Drizzle ORM (Type-safe queries, Migration management)
  • Payment Subscriptions: Creem integration (One-time payments, Subscriptions, Customer portal, Webhooks)
  • File Upload: Cloudflare R2 integration (Client-side presigned direct upload, Server-side proxy upload, Image compression)
  • Content Management: Keystatic (Markdown/MDX blog system)
  • Email Service: Resend + React Email (Transactional email templates)
  • Form Handling: React Hook Form + Zod (Type-safe form validation)
  • Code Quality: ESLint, Prettier
  • Admin Dashboard: Generic data management dashboard, easily extensible to manage any database table
  • Deployment: One-click Vercel deployment

1.4. Technical Architecture Diagram

Technical Architecture Diagram](https://mermaid.live/edit#pako:eNqNk1tPwjAUx79K09cRkuVSLqsv-g1MfDBkw8TEB1rOHGMd2zLWQkhCmAlKXP7bFr6F09Ol59d-_Z_Tt8N6DhoCVlSHoA3IqmKCU3-JOHy2hHhLUWOZ-_Ek1pj1Qv-KCWp7vdfJlsIe20g-kXGXGjQpIqQiEpMUpGOGJB9JGhCdhRJJWnNwCuI_gdWCTfZH5pB6Cri_7j6Fz5vF-Ow3SrqLAHaMvBVSLGj0bYG6vWm0LcSgBm0A6ZKjmzJdLlFmQgQZ8VsXuPFd4x4GzWfH6VpXILdvuJBsWKjXIW3ywQoTqPRNWDcnW3UqaEpGOzBW-a6V7N3x0wIgqtd6I3_sxJ6-77jEtJXHBhJ47QvqUpEVX6VCRBKrn2RfqDgvQU-XfgQsG3RFgNpj4Hx2v4DH7ZPQR2X8aRlQdGWKoKGbWXhQZJG3k4LhlDVs6Rf6QFyS6qYWWKGV4jZp4rJPJ1kVWJA6wPdg0r3qWANyf7K5yJT3rn7-Acr4J7o)

graph TD
    subgraph "Frontend (Browser)"
        A[User] --> B{Next.js App};
    end

    subgraph "Vercel Platform"
        B -- React Server Components --> C["UI (shadcn/ui, Tailwind)"];
        B -- API Routes/Server Actions --> D[Backend Logic];
    end

    subgraph "Core Services"
        D -- ORM --> E[Drizzle ORM];
        E --> F[(PostgreSQL)];
        D -- Auth API --> G[Better-Auth];
        D -- Payment API --> H[Creem];
        D -- Email API --> I[Resend];
        D -- Storage API --> J[Cloudflare R2];
    end
    
    subgraph "Content Management (CMS)"
        K[Keystatic] -- Reads/Writes --> L[Markdown/JSON in Git];
        B -- Reads data --> L;
    end
    
    G -- OAuth --> M[Google/GitHub/LinkedIn];
    H -- Webhooks --> D;

    style A fill:#f9f,stroke:#333,stroke-width:2px;
    style F fill:#add,stroke:#333,stroke-width:2px;
    style J fill:#f90,stroke:#333,stroke-width:2px;
    style H fill:#f66,stroke:#333,stroke-width:2px;
    style I fill:#9cf,stroke:#333,stroke-width:2px;

2. In-Depth Technical Analysis

2.1. Directory Structure Breakdown

SaaS-Starter-main/
├── src/                  # All application source code
│   ├── app/              # Next.js App Router core directory
│   │   ├── (auth)/       # Authentication-related pages (login, signup)
│   │   ├── (dashboard)/  # Protected dashboard pages
│   │   ├── (pages)/      # Public pages (home, about, blog, etc.)
│   │   ├── api/          # API routes
│   │   ├── keystatic/    # Keystatic CMS admin interface
│   │   ├── layout.tsx    # Root layout
│   │   └── not-found.tsx # Global 404 page
│   ├── components/       # React components
│   │   ├── admin/        # Admin dashboard components
│   │   ├── auth/         # Authentication flow components
│   │   ├── blog/         # Blog-related components
│   │   ├── forms/        # Form components
│   │   ├── homepage/     # Homepage-specific components
│   │   └── ui/           # Generic UI components (based on shadcn/ui)
│   ├── database/         # Drizzle ORM related
│   │   ├── migrations/   # Database migration files
│   │   ├── config.ts     # Development migration config
│   │   ├── config.prod.ts # Production migration config
│   │   ├── index.ts      # Drizzle client initialization
│   │   └── schema.ts     # Database table structure definitions
│   ├── emails/           # React Email templates
│   ├── hooks/            # Custom React Hooks
│   ├── lib/              # Core logic and utility functions
│   │   ├── actions/      # Next.js Server Actions
│   │   ├── admin/        # Admin dashboard core logic
│   │   ├── auth/         # Authentication config and logic (Better-Auth)
│   │   ├── billing/      # Payment abstraction layer and providers (Creem)
│   │   ├── config/       # Global constants, products, roles, etc.
│   │   ├── database/     # Database helper functions
│   │   ├── email.tsx     # Email sending service
│   │   └── r2.ts         # Cloudflare R2 file upload service
│   ├── schemas/          # Zod validation schemas
│   └── types/            # TypeScript type definitions
├── content/              # Keystatic-managed content (Markdown, JSON)
├── public/               # Static assets
├── scripts/              # Helper scripts (like setting up admin)
└── styles/               # Global styles and CSS

2.2. Core Module Analysis

2.2.1. Entry Files and Startup Flow

  • src/app/layout.tsx: The project's root layout that wraps all pages. It handles:
    • Setting HTML lang attribute and font variables.
    • Integrating ThemeProvider for dark/light mode.
    • Integrating NextTopLoader for page loading progress.
    • Integrating Toaster for global notifications.
    • Integrating CookieConsent for cookie consent management.
  • middleware.ts: Runs before requests reach pages, core for route protection.
    • Checks user session cookies.
    • Redirects to /login if user is not logged in but accessing /dashboard/*.
    • Redirects to /dashboard if user is logged in but accessing /login or /signup.
  • src/app/dashboard/layout.tsx: Root layout for the dashboard.
    • Uses SessionGuard component to protect all child routes. SessionGuard is a client component that verifies session existence, redirects to login if not found, and shows loading animation during verification.
    • Renders AppSidebar and main content area SidebarInset.

2.2.2. Configuration System Design

The project's configuration is highly centralized for easy maintenance and extension.

  • Environment Variables (env.ts): Uses @t3-oss/env-nextjs to enforce environment variable validation. All environment variables (like API keys, database URLs) must be defined in the .env file and accessed through env.ts for type safety. This prevents runtime errors due to missing environment variables.
  • Application Constants (src/lib/config/constants.ts): Stores app name, description, contact email, and other hardcoded values that don't change frequently.
  • Product Plans (src/lib/config/products.ts): Centrally defines all paid plans. Each plan includes internal ID, name, feature list, and product IDs in different payment providers (like Creem). This structure makes it easy to add new plans or switch payment providers.
  • User Roles (src/lib/config/roles.ts): Defines user roles and their hierarchical relationships (user, admin, super_admin). Helper functions like hasRole provide unified permission checking logic.
  • File Upload (src/lib/config/upload.ts): Centrally manages all file upload rules, including maximum file size, allowed file types, etc. All upload paths (client and server-side) share this configuration, ensuring rule consistency.

2.2.3. Routing Architecture

The project uses Next.js App Router and leverages Route Groups for logical page separation.

  • (pages): Contains all public pages like home, about, blog, pricing, etc. Uses src/app/(pages)/layout.tsx to provide unified header and footer.
  • (auth): Contains authentication flow pages like login, signup. Uses src/app/(auth)/layout.tsx to provide a centered, clean layout.
  • (dashboard): Contains all pages requiring user login. Its layout src/app/dashboard/layout.tsx implements route protection through SessionGuard.

2.2.4. Build and Packaging Process

  • next.config.ts: Next.js core configuration file.
    • Configures images.remotePatterns to allow loading images from Unsplash and Cloudflare R2.
    • Integrates @next/bundle-analyzer. When ANALYZE environment variable is set to true, running pnpm analyze generates and opens bundle size analysis report after build, helping developers optimize frontend resource size.
  • package.json:
    • dev: Starts development server with Next.js 15's --turbo mode for faster local compilation.
    • build: Builds production application.
    • start: Starts production server.

3. Development Guide

3.1. Environment Setup

  1. Install Tools:
    • Node.js v20.x or higher.
    • pnpm (npm install -g pnpm).
    • PostgreSQL database (recommended using Docker: docker run --name my-postgres -e POSTGRES_PASSWORD=mysecretpassword -p 5432:5432 -d postgres).
  2. Clone and Install:
    git clone https://github.com/ullrai/saas-starter.git
    cd saas-starter
    pnpm install
    
  3. Configure Environment Variables:
    • Copy .env.example to .env.
    • Generate a secure BETTER_AUTH_SECRET: openssl rand -base64 32.
    • Fill in your PostgreSQL DATABASE_URL.
    • Register and obtain API keys for Creem, Resend, Cloudflare R2, and fill them in the .env file.
  4. Database Setup:
    • Development: pnpm db:push synchronizes changes from database/schema.ts directly to the database, suitable for rapid iteration.
    • Production: Must use migration files. Process:
      1. pnpm db:generate:prod: Generate production migration SQL files.
      2. Deploy code and migration files to production.
      3. Run pnpm db:migrate:prod in production to apply migrations.

3.2. Development Workflow

  1. Start Development Server: pnpm dev
  2. Modify Database:
    • Edit database/schema.ts.
    • Run pnpm db:push to sync changes.
  3. Create New Pages:
    • Create new folders and page.tsx files in app/(pages) or app/(dashboard).
  4. Create API Routes:
    • Create new folders and route.ts files in the app/api directory.
  5. Create Server Actions:
    • Create new files in the lib/actions directory using the "use server"; directive.
  6. Code Checking:
    • Run pnpm lint to check code style.
    • Run pnpm prettier:format to format code.

3.3. Code Standards

  • Naming Conventions:
    • Components use PascalCase, e.g., FileUploader.
    • Functions and variables use camelCase.
    • Constants use UPPER_SNAKE_CASE.
  • File Organization:
    • Page components are placed in their respective app route folders, usually in _components subdirectories.
    • Reusable components are placed in the components directory.
    • Logic, types, configurations are separated into lib, types, schemas directories.
  • Comment Requirements:
    • Use JSDoc comments for complex functions or logic blocks.
    • Use inline comments for non-intuitive code.

4. Feature Module Details

4.1. Authentication System (Better-Auth)

This starter kit uses the better-auth library to provide a complete authentication solution.

  • Core Configuration: src/lib/auth/server.ts
    • Configures Drizzle database adapter.
    • Dynamically loads social login providers (Google, GitHub, LinkedIn), only enabled when corresponding CLIENT_ID and SECRET are provided in .env.
    • Integrates magicLink plugin and configures using Resend for email sending.
  • API Route: app/api/auth/[...all]/route.ts
    • This is a dynamic route that captures all better-auth authentication requests (like /api/auth/magic-link, /api/auth/google/login, etc.) and hands them to auth.handler.
  • Client: src/lib/auth/client.ts
    • Provides methods for interacting with the authentication system in client components, like signIn, signOut, useSession, etc.
  • Authentication Flow (Magic Link): Magic Link
    sequenceDiagram
        participant User
        participant Client as Frontend (AuthForm)
        participant Server as Server (API)
        participant Resend as Email Service
    
        User->>Client: Enter email and click login
        Client->>Server: POST /api/auth/magic-link
        Server->>Server: Generate time-limited Token
        Server->>Resend: Request to send email (with Token URL)
        Resend-->>User: Send magic link email
        User->>User: Click link in email
        Client->>Server: GET /api/auth/callback?token=...
        Server->>Server: Verify Token, create session
        Server-->>Client: Set session Cookie and redirect to /dashboard
    

4.2. Database & ORM (Drizzle)

  • Schema Definition: database/schema.ts is the single source of truth for all database tables, defining table structures, relationships, and constraints using Drizzle ORM syntax.
  • Client Initialization: database/index.ts initializes the Drizzle client and applies different connection pool configurations based on environment (Serverless or traditional server) (src/lib/database/connection.ts).
  • Migration Management:
    • The project maintains two separate migration histories for development and production environments, located in database/migrations/development and database/migrations/production respectively.
    • pnpm db:generate & pnpm db:generate:prod: Generate SQL migration files based on changes in schema.ts.
    • pnpm db:push: Development only, directly syncs schema to database, loses history.
    • pnpm db:migrate:dev & pnpm db:migrate:prod: Apply migration files to database.

4.3. Payment & Subscriptions (Creem)

  • Abstraction Layer: src/lib/billing/index.ts exports a unified billing object, making it easy to switch to other payment providers (like Stripe) in the future without modifying upper-level business code.
  • Provider Implementation: src/lib/billing/creem/provider.ts is the specific implementation for Creem payment provider, encapsulating logic for creating checkout sessions, customer portals, and handling webhooks.
  • API Interfaces:
    • /api/billing/checkout: Creates payment sessions. Returns 409 Conflict status and management link when user tries to purchase existing subscription.
    • /api/billing/portal: Creates a URL to Creem customer portal where users can manage their subscriptions.
    • /api/billing/webhooks/creem: Receives webhook events from Creem for updating subscription status, recording payments, etc.
  • Webhook Handling: src/lib/billing/creem/webhook.ts
    • Security: Uses crypto.timingSafeEqual to verify webhook signatures, preventing forged requests.
    • Idempotency: Records processed event IDs in webhook_events table to prevent duplicate processing of the same event.
    • Transactional: All database operations are completed in one transaction, ensuring data consistency.
  • Payment Flow: Payment Flow
    sequenceDiagram
        participant User
        participant Client as Frontend (Pricing Page)
        participant Server as Server
        participant Creem
    
        User->>Client: Click "Get Plan"
        Client->>Server: POST /api/billing/checkout
        Server->>Creem: Create Checkout Session
        Creem-->>Server: checkoutUrl
        Server-->>Client: Return checkoutUrl
        Client->>User: Redirect to Creem payment page
        User->>Creem: Complete payment
        Creem-->>Server: Webhook (checkout.completed)
        Server->>Server: Verify signature, record event
        Server->>Server: (DB Transaction) Update user subscription status
        User->>Client: Redirect to /payment-status
    

4.4. File Upload (Cloudflare R2)

The system supports two upload modes, providing optimal choices for different scenarios. All upload rules are centralized in src/lib/config/upload.ts.

4.4.1. Client-side Presigned Upload (UI Recommended)

This is the default method used by the FileUploader component, offering better performance.

Flow Diagram:

Presigned Upload

sequenceDiagram
    participant User
    participant FileUploader as Frontend Component
    participant Server as Server API
    participant R2 as Cloudflare R2
    
    User->>FileUploader: Select/drag files
    FileUploader->>FileUploader: Client-side validation (type/size), image compression
    FileUploader->>Server: POST /api/upload/presigned-url (request upload URL)
    Server->>Server: Verify identity and file metadata
    Server->>R2: Request presigned URL
    R2-->>Server: Return presigned URL
    Server-->>FileUploader: Return presigned URL
    FileUploader->>R2: PUT (direct file upload)
    R2-->>FileUploader: Upload success
    FileUploader->>FileUploader: onUploadComplete callback

4.4.2. Server-side Proxy Upload

This mode allows server-side processing before storage.

Flow Diagram:

Server-side Upload

sequenceDiagram
    participant Client as Client/Script
    participant Server as Server API
    participant R2 as Cloudflare R2
    
    Client->>Server: POST /api/upload/server-upload (multipart/form-data)
    Server->>Server: Verify identity and file
    Server->>R2: Stream file
    R2-->>Server: Upload success
    Server->>Server: Record to database
    Server-->>Client: Return upload result

4.5. Blog & Content Management (Keystatic)

  • CMS: Uses Keystatic as Git-based CMS, all content stored in Markdown and JSON files under content/ directory.
  • Admin Interface: In development environment, access /keystatic to enter admin dashboard. For security, this interface is disabled by default in production.
  • Content Reading:
    • @keystatic/core/reader used for safely reading content under content/ directory on server-side.
    • src/app/(pages)/blog/page.tsx: Blog list page, reads all articles.
    • src/app/(pages)/blog/[slug]/page.tsx: Blog detail page, reads single article and uses @markdoc/markdoc to parse Markdoc content.

4.6. Admin Dashboard

Provides a powerful, extensible data management system.

  • Generic Table Manager: components/admin/generic-table/generic-table-manager.tsx is a core component that can automatically generate a complete CRUD management interface for any table declared in src/lib/config/admin-tables.ts.
  • Configuration-Driven:
    1. Add Drizzle table object in src/lib/config/admin-tables.ts.
    2. Add navigation link in genericTableNavigation in src/app/dashboard/_components/app-sidebar.tsx.
    3. Access new management page at /dashboard/admin/tables/[tableName].
  • Server Actions: All CRUD operations completed through type-safe Server Actions in src/lib/actions/admin-generic.ts, no need to write additional API routes.
  • Schema Parsing: src/lib/admin/schema-generator.ts dynamically parses Drizzle schema, automatically generates forms and validation rules (Zod), greatly simplifying backend development.

5. Secondary Development Guide

5.1. Extension Point Identification

  • Add New Pages: Create new routes in app/(pages) or app/(dashboard).
  • Add New Admin Management Tables:
    1. Define new table in database/schema.ts.
    2. Register the table in enabledTablesMap in src/lib/config/admin-tables.ts.
    3. Add navigation link in src/app/dashboard/_components/app-sidebar.tsx.
  • Add New Payment Provider:
    1. Create new provider implementation file under src/lib/billing/, must follow PaymentProvider interface in src/lib/billing/provider.ts.
    2. Modify PAYMENT_PROVIDER logic in src/lib/billing/index.ts to switch to new provider.
  • Customize Email Templates: Create or modify React Email components in src/emails/ directory.
  • Customize UI Components: Modify shadcn/ui components or add new ones in src/components/ui/.

5.2. API Reference

RouteMethodDescription
/api/auth/[...all]GET, POSTHandle all better-auth authentication requests.
/api/billing/checkoutPOSTCreate payment session.
/api/billing/portalGETGet customer portal URL.
/api/billing/webhooks/creemPOSTReceive Creem webhook events.
/api/upload/presigned-urlPOSTGet presigned URL for client-side direct upload.
/api/upload/server-uploadPOSTServer-side proxy file upload.
/api/payment-statusGETQuery payment status.
/api/keystatic/[...params]GET, POSTKeystatic CMS API interface (development only).

5.3. Hooks and Events

  • useSidebar(): Used in dashboard components to control sidebar expand/collapse state.
  • useIsMobile(): Client-side hook to determine if current device is mobile size, safe for responsive components, avoiding SSR hydration errors.
  • useAdminTable(): Core hook for driving admin dashboard table components. Encapsulates data fetching, pagination, search, filtering, and loading state management logic.
  • onUploadComplete: Callback prop for FileUploader component, triggered after successful file upload.

6. Developer Toolchain

6.1. Testing Strategy

  • Framework: Uses Jest and React Testing Library.
  • Configuration Files: jest.config.ts, jest.setup.ts.
  • Examples: Project includes unit test examples for UI components (logo.test.tsx), layouts (layout.test.tsx), schemas (auth.schema.test.ts), and core logic (database/index.test.ts).
  • Run Tests: pnpm test

6.2. Code Quality Assurance

  • ESLint: Configured in .eslintrc.json, follows eslint-config-next best practices.
  • Prettier: Integrated with ESLint, uses prettier-plugin-tailwindcss to auto-sort Tailwind CSS classes.
  • Run Checks: pnpm lint and pnpm prettier:check.
  • Auto Format: pnpm prettier:format.

6.3. Bundle Size Analysis

  • Uses @next/bundle-analyzer to analyze production build bundle size.
  • Run pnpm analyze to generate client and server analysis reports.
  • This is crucial for identifying and optimizing large dependencies.

7. Real-world Application Scenarios

7.1. Typical Use Cases

  • Enterprise SaaS: As starting point for new projects, integrates user management, role permissions, payments, and audit logs (through webhook events) needed by enterprises.
  • AI Applications: Quickly build AI tools requiring user login and usage/subscription-based payments. File upload functionality can be used for processing user data.
  • Paid Content Platforms: Blog and content management system combined with payment functionality can easily be extended to paid content platforms.
  • Internal Tools: Leverage powerful admin dashboard and data management capabilities to quickly build company internal data management tools or dashboards.

8. Utility Tools

8.1. CLI Commands

ScriptDescription
pnpm devStart development server (Turbo mode)
pnpm buildBuild production application
pnpm startStart production server
pnpm lintRun ESLint checks
pnpm testRun Jest unit tests
pnpm prettier:formatFormat all code
pnpm db:generateGenerate migration files for development
pnpm db:generate:prodGenerate migration files for production
pnpm db:migrate:devApply development migrations
pnpm db:migrate:prodApply production migrations
pnpm db:push(Development only) Push schema to database
pnpm analyzeBuild and analyze bundle size
pnpm set:admin(Local) Promote user to super admin
pnpm set:admin:prod(Production) Promote user to super admin

8.2. Configuration Options

All required and optional environment variables are detailed in the environment configuration section of README.md. Be sure to completely fill out the .env file.

8.3. Utility Functions

src/lib/utils.ts provides some useful utility functions:

  • cn(...inputs): Safely merge Tailwind CSS class names and resolve conflicts.
  • formatCurrency(amount, currency): Format amounts in cents to currency strings.
  • calculateReadingTime(text): Calculate estimated reading time based on text content.
  • renderMarkdoc(node): Convert Markdoc AST nodes to plain text strings for generating summaries.

9. Version Management & Updates

9.1. Dependency Management

  • Package Manager: Project uses pnpm, ensure you have it installed globally. pnpm leverages content-addressable storage to save disk space and speed up installations.
  • Version Locking: pnpm-lock.yaml file locks exact versions of all dependencies and their sub-dependencies, ensuring consistency across team members and different deployment environments.
  • Dependency Updates: Recommend using pnpm up --latest to safely update dependencies, and pay attention to major version change logs.

10. Best Practices

10.1. Performance Optimization

  • Code Splitting: Use next/dynamic for dynamic imports of large components, like dynamic imports for each settings page in src/app/dashboard/settings/_components/settings.tsx.
  • Image Optimization: Prioritize using Next.js <Image> component, which automatically performs image size optimization, format conversion (like WebP), and lazy loading.
  • Server Components: Use React Server Components (RSC) as much as possible for data fetching and logic execution, reducing JavaScript code sent to client.
  • Database Queries: Avoid executing database queries in loops. Leverage Drizzle ORM's join and batch operation capabilities to reduce database round trips.

10.2. Security Considerations

  • Environment Variables: Never commit .env file to Git repository. Use secret management tools from platforms like Vercel to store production environment variables.
  • Route Protection: middleware.ts is the first line of defense, but must use functions like requireAuth, requireAdmin in Server Actions and API routes for backend permission verification.
  • SQL Injection: Using Drizzle ORM effectively prevents SQL injection attacks because it automatically parameterizes queries.
  • XSS: Next.js and React escape JSX content by default, preventing cross-site scripting attacks. When handling user-generated content, use mature libraries (like DOMPurify) for sanitization.
  • Webhook Security: Signature verification in src/lib/billing/creem/webhook.ts is key to ensuring webhook requests come from trusted sources.

10.3. Deployment Guide

Vercel deployment is recommended.

  1. Push your code to GitHub/GitLab/Bitbucket repository.
  2. Import the Git repository in Vercel.
  3. Vercel will automatically detect Next.js project and configure build settings.
  4. In Vercel project's Settings > Environment Variables, add all environment variables defined in your .env file.
  5. Configure production database migration process in your CI/CD pipeline, ensuring pnpm db:migrate:prod runs after each successful deployment.
  6. Each push to main branch will automatically build and deploy your application on Vercel.

11. Community & Ecosystem

11.1. Community Resources

11.2. Contribution Guidelines

We welcome community contributions!

  1. Fork this project repository.
  2. Create a new branch (git checkout -b feature/your-feature-name).
  3. Make changes and commit (git commit -m 'feat: Add some feature').
  4. Push your branch to forked repository (git push origin feature/your-feature-name).
  5. Create a Pull Request.

12. Troubleshooting

12.1. Common Issues FAQ

Q: Why can't I access the /keystatic admin dashboard?

A: Keystatic admin interface is only enabled in development environment (NODE_ENV=development) by default for security. You need to run pnpm dev locally to access it.

Q: File upload fails with CORS error.

A: This is the most common file upload issue. Make sure you have correctly configured CORS policy in your Cloudflare R2 bucket settings, allowing PUT and GET requests from your deployment domain and http://localhost:3000.

Q: How to set up the first admin account?

A: The system doesn't automatically set up admins. You need to:

  1. First register an account normally in the app with the email you want to make admin.
  2. Run pnpm set:admin [email protected] (local) or pnpm set:admin:prod [email protected] (production) in your project root directory.

Q: Social login doesn't work, what to do?

A: Please check the following:

  1. Make sure you correctly filled in the corresponding social platform's CLIENT_ID and CLIENT_SECRET in the .env file.
  2. Make sure in the social platform's OAuth app configuration (like Google Cloud Console, GitHub Developer Settings), you've added http://localhost:3000/api/auth/[provider]/callback and your production domain's callback URL to the authorized callback URL list.

Thanks for reading!

Want to read more articles? Check out our blog for the latest insights and updates.