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
](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.
- Setting HTML
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 areaSidebarInset
.
- Uses
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 throughenv.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 likehasRole
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. Usessrc/app/(pages)/layout.tsx
to provide unified header and footer.(auth)
: Contains authentication flow pages like login, signup. Usessrc/app/(auth)/layout.tsx
to provide a centered, clean layout.(dashboard)
: Contains all pages requiring user login. Its layoutsrc/app/dashboard/layout.tsx
implements route protection throughSessionGuard
.
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
. WhenANALYZE
environment variable is set totrue
, runningpnpm analyze
generates and opens bundle size analysis report after build, helping developers optimize frontend resource size.
- Configures
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
- 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
).
- Clone and Install:
git clone https://github.com/ullrai/saas-starter.git cd saas-starter pnpm install
- 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.
- Copy
- Database Setup:
- Development:
pnpm db:push
synchronizes changes fromdatabase/schema.ts
directly to the database, suitable for rapid iteration. - Production: Must use migration files. Process:
pnpm db:generate:prod
: Generate production migration SQL files.- Deploy code and migration files to production.
- Run
pnpm db:migrate:prod
in production to apply migrations.
- Development:
3.2. Development Workflow
- Start Development Server:
pnpm dev
- Modify Database:
- Edit
database/schema.ts
. - Run
pnpm db:push
to sync changes.
- Edit
- Create New Pages:
- Create new folders and
page.tsx
files inapp/(pages)
orapp/(dashboard)
.
- Create new folders and
- Create API Routes:
- Create new folders and
route.ts
files in theapp/api
directory.
- Create new folders and
- Create Server Actions:
- Create new files in the
lib/actions
directory using the"use server";
directive.
- Create new files in the
- Code Checking:
- Run
pnpm lint
to check code style. - Run
pnpm prettier:format
to format code.
- Run
3.3. Code Standards
- Naming Conventions:
- Components use PascalCase, e.g.,
FileUploader
. - Functions and variables use camelCase.
- Constants use UPPER_SNAKE_CASE.
- Components use PascalCase, e.g.,
- 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.
- Page components are placed in their respective
- 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
andSECRET
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 toauth.handler
.
- This is a dynamic route that captures all
- Client:
src/lib/auth/client.ts
- Provides methods for interacting with the authentication system in client components, like
signIn
,signOut
,useSession
, etc.
- Provides methods for interacting with the authentication system in client components, like
- Authentication Flow (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
anddatabase/migrations/production
respectively. pnpm db:generate
&pnpm db:generate:prod
: Generate SQL migration files based on changes inschema.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.
- The project maintains two separate migration histories for development and production environments, located in
4.3. Payment & Subscriptions (Creem)
- Abstraction Layer:
src/lib/billing/index.ts
exports a unifiedbilling
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. Returns409 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.
- Security: Uses
- 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:
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:
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 undercontent/
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 undercontent/
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 insrc/lib/config/admin-tables.ts
. - Configuration-Driven:
- Add Drizzle table object in
src/lib/config/admin-tables.ts
. - Add navigation link in
genericTableNavigation
insrc/app/dashboard/_components/app-sidebar.tsx
. - Access new management page at
/dashboard/admin/tables/[tableName]
.
- Add Drizzle table object in
- 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)
orapp/(dashboard)
. - Add New Admin Management Tables:
- Define new table in
database/schema.ts
. - Register the table in
enabledTablesMap
insrc/lib/config/admin-tables.ts
. - Add navigation link in
src/app/dashboard/_components/app-sidebar.tsx
.
- Define new table in
- Add New Payment Provider:
- Create new provider implementation file under
src/lib/billing/
, must followPaymentProvider
interface insrc/lib/billing/provider.ts
. - Modify
PAYMENT_PROVIDER
logic insrc/lib/billing/index.ts
to switch to new provider.
- Create new provider implementation file under
- Customize Email Templates: Create or modify React Email components in
src/emails/
directory. - Customize UI Components: Modify
shadcn/ui
components or add new ones insrc/components/ui/
.
5.2. API Reference
Route | Method | Description |
---|---|---|
/api/auth/[...all] | GET, POST | Handle all better-auth authentication requests. |
/api/billing/checkout | POST | Create payment session. |
/api/billing/portal | GET | Get customer portal URL. |
/api/billing/webhooks/creem | POST | Receive Creem webhook events. |
/api/upload/presigned-url | POST | Get presigned URL for client-side direct upload. |
/api/upload/server-upload | POST | Server-side proxy file upload. |
/api/payment-status | GET | Query payment status. |
/api/keystatic/[...params] | GET, POST | Keystatic 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 forFileUploader
component, triggered after successful file upload.
6. Developer Toolchain
6.1. Testing Strategy
- Framework: Uses
Jest
andReact 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
, followseslint-config-next
best practices. - Prettier: Integrated with ESLint, uses
prettier-plugin-tailwindcss
to auto-sort Tailwind CSS classes. - Run Checks:
pnpm lint
andpnpm 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
Script | Description |
---|---|
pnpm dev | Start development server (Turbo mode) |
pnpm build | Build production application |
pnpm start | Start production server |
pnpm lint | Run ESLint checks |
pnpm test | Run Jest unit tests |
pnpm prettier:format | Format all code |
pnpm db:generate | Generate migration files for development |
pnpm db:generate:prod | Generate migration files for production |
pnpm db:migrate:dev | Apply development migrations |
pnpm db:migrate:prod | Apply production migrations |
pnpm db:push | (Development only) Push schema to database |
pnpm analyze | Build 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 insrc/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 likerequireAuth
,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.
- Push your code to GitHub/GitLab/Bitbucket repository.
- Import the Git repository in Vercel.
- Vercel will automatically detect Next.js project and configure build settings.
- In Vercel project's
Settings > Environment Variables
, add all environment variables defined in your.env
file. - Configure production database migration process in your CI/CD pipeline, ensuring
pnpm db:migrate:prod
runs after each successful deployment. - Each push to main branch will automatically build and deploy your application on Vercel.
11. Community & Ecosystem
11.1. Community Resources
- Official Repository: UllrAI SaaS Starter on GitHub
- Issues & Discussions: Use GitHub Issues to submit bug reports and feature requests.
- Main Dependency Documentation:
11.2. Contribution Guidelines
We welcome community contributions!
- Fork this project repository.
- Create a new branch (
git checkout -b feature/your-feature-name
). - Make changes and commit (
git commit -m 'feat: Add some feature'
). - Push your branch to forked repository (
git push origin feature/your-feature-name
). - 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:
- First register an account normally in the app with the email you want to make admin.
- Run
pnpm set:admin [email protected]
(local) orpnpm 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:
- Make sure you correctly filled in the corresponding social platform's
CLIENT_ID
andCLIENT_SECRET
in the.env
file. - 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.