Last Updated: April 18, 2026 Framework: Next.js 16.2.2 (App Router) Runtime: Bun 1.3.9 Status: Production Ready (9.5/10)
- Executive Summary
- Tech Stack
- Project Structure
- Data Flow & Architecture
- Core Components
- API Routes & Server Actions
- Database Schema
- Authentication & Authorization
- Security Features
- Monitoring & Observability
- State Management
- File Uploads & Storage
- SEO & Metadata
- Testing & CI/CD
- Environment Configuration
- Deployment Checklist
Beeclean is a professional iPhone cleaning & optimization web application built with modern Next.js 16. The app follows a Feature-Sliced Design / Domain-Driven Architecture, keeping domain-specific logic tightly grouped. It utilizes Next.js Server Actions for optimized database mutation entirely bypassing the REST overhead.
The app features:
- Public Website: Marketing pages, blog, career/job listings, dynamic newsletter subscription.
- Admin Dashboard: Blog management, application review, job posting, newsletter subscriber tracking.
- Architecture: Feature-Sliced Design (
features/directory) with Next.js Server Actions. - Authentication: Email/password via Better-Auth.
- Content Management: Blog system with tags, categories, rich content.
- Job Applications: Form with resume upload, admin review workflow.
- Security: Enterprise-grade (CSRF, XSS protection, admin authorization, rate limiting).
- Monitoring: Sentry error tracking, Web Vitals, health checks.
- Framework: Next.js 16.2.2 (App Router, React 19)
- Styling: Tailwind CSS v4, shadcn/ui components
- Animations: Framer Motion v12
- State Management: TanStack Query v5 (React Query)
- URL State: Nuqs (query string management)
- Runtime: Node.js (with Bun package manager)
- Architecture Flow: Next.js Server Actions + direct database interfacing
- ORM: Prisma 7.6 (PostgreSQL)
- Authentication: Better-Auth 1.5
- Validation: Zod 4.3
- Security: DOMPurify (XSS prevention)
- Rate Limiting: Upstash Redis (sliding window)
- Monitoring: Sentry 10.47 (error tracking)
- File Storage: Local filesystem (public/uploads/)
- Deployment: Vercel-compatible (Node.js)
- CI/CD: GitHub Actions
- Testing: Vitest 4.1 + Testing Library
- Fonts: Satoshi (headings), Inter (body)
- Colors: White/gray palette with Apple-style shadows / Black & White premium aesthetic.
- Radius: Cards: 24px (rounded-3xl), Buttons: 16px (rounded-xl)
- Spacing: Container max-w-7xl, section padding py-20/pt-30 pb-20
The project has been scaled into a feature-sliced architecture. The app/ router strictly handles routing, while the UI, business logic, and custom hooks are isolated natively within features/.
beeclean-framer/
βββ app/ # Next.js App Router (Routing Engine)
β βββ (auth)/ # Authentication route group
β βββ actions/ # Next.js Server Actions for Mutations/Fetching
β β βββ applications.ts
β β βββ blogs.ts
β β βββ careers.ts
β β βββ newsletter.ts
β βββ api/ # Reserved for external & structural APIs
β β βββ auth/ # Better-auth endpoints
β β βββ health/route.ts # GET (health check)
β βββ blogs/
β β βββ [slug]/page.tsx # Dynamic blog post rendering
β β βββ page.tsx # Renders features/blogs/ui/pages/blogs.tsx
β βββ career/
β β βββ page.tsx # Renders features/career/ui/pages/careers.tsx
β βββ dashboard/ # Admin Panel pages
β βββ download/
β βββ privacy-policy/
β βββ terms-conditions/
β βββ layout.tsx # Root layout (fonts, providers, error boundary)
β βββ page.tsx # Renders features/home/ui/pages/home.tsx
β βββ sitemap.ts # Dynamic sitemap
β βββ robots.ts # Robots.txt configuration
β
βββ features/ # Feature-Sliced App Domains
β βββ auth/ # Authentication Logic & UI
β βββ blogs/ # Blog viewing and listing
β β βββ hooks/
β β βββ ui/pages/
β βββ career/ # Jobs, application submittions
β β βββ hooks/
β β βββ ui/pages/
β βββ dashboard/ # Admin CMS and Analytics
β β βββ hooks/
β β β βββ use-dashboard-applications.ts
β β β βββ use-dashboard-blogs.ts
β β β βββ use-dashboard-careers.ts
β β β βββ use-dashboard-newsletter.ts
β β βββ ui/pages/
β βββ home/ # Landing Page components & layout
β βββ legal/ # Privacy policy, T&C
β βββ ui/pages/
β
βββ components/ # Shared Presentational Elements
β βββ layout/ # Navbar, Footer
β βββ ui/ # shadcn/ui generic components (Button, Dialog...)
β βββ canvas/ # Shared generic canvas components
β βββ error-boundary.tsx # Global error catcher
β βββ web-vitals.tsx # Analytics
β
βββ hooks/ # Generic/Utility hooks
β βββ use-mobile.ts # Global responsive identifier
β
βββ lib/
β βββ auth.ts # Better-Auth configuration
β βββ auth-client.ts # Client-side auth client
β βββ env-validator.ts # Environment variable validation
β βββ prisma.ts # Prisma singleton instance
β βββ react-query.tsx # TanStack Query provider
β βββ ratelimit.ts # Rate limiting (Upstash Redis)
β βββ validators.ts # Zod schemas
β βββ ...
β
βββ prisma/
β βββ schema.prisma # Database schema
β βββ seed.ts # Database seeding
β βββ migrations/ # Migration history
β
βββ public/ # Static assets
βββ ... (Configuration files)
User Request β Next.js 16 (App Router) β Client Hook (TanStack Query) β Next.js Server Action (`app/actions/*`) β Database (Prisma) β Response
- Feature-Sliced Architecture: All pages under
app/strictly import<PageName />components fromfeatures/[domain]/ui/pages/. This keepsapp/clean. - Server Actions vs API Routes: Previously, Data was fetched via REST API routes (
app/api/*). Now, all client data mutation and data fetching is resolved using strictly typed Server Actions inapp/actions/, maximizing SSR capabilities and removing fetch overhead. - Data Fetching: TanStack Query handles caching data client-side, heavily utilizing Server Actions as query functions.
- Validation: Zod schemas validate Server Action
FormDataand typical payloads immediately. - Authorization: Server Actions inherently check
await auth.api.getSession()and the custom validation functionisAdmin(). - Error Handling: Graceful nested
try/catchwith structured object outputs returned to the browser clients. - Rate Limiting: Upstash Redis sliding window limits requests to mutating Server Actions to mitigate bot/spam usage.
Purpose: Application-wide layout and provider setup
Key Features:
- Font configuration (Inter, Satoshi)
- NuqsAdapter (query string state management)
- ErrorBoundary (error catching)
- QueryProvider (TanStack Query)
- WebVitals tracking
The components/ directory is reserved strictly for primitive, structural UI components without domain knowledge:
components/ui/-> generic shadcn elements (buttons, inputs, sonner).components/layout/-> Generic navbars, footers.
For any specific domain (e.g., Application forms, Blog Cards) β they live dynamically inside their respective features/[domain]/ui/ directories.
With the migration from /api REST endpoints, business endpoints are now executed safely within Next.js Server Actions. The only endpoints left under /api are specific external webhooks or architectural defaults (/api/auth, /api/health).
Fetches published blogs with optional tag filtering.
Fetches single published blog by slug. Returns 404 behavior if not public.
Submits job application with resume upload. Validation: Validates file size (10MB max), MIME type (PDF/DOC/DOCX via magic bytes) locally before uploading the document logic. Rate-limited by IP.
Fetches all published jobs.
Validates an email using Zod, establishes checking for an existing record, limits requests using Upstash Ratelimiting, and stores it in the Postgres database.
getApplications(): Fetches job apps.updateApplicationStatus(id, status): Transitions status (pending, accepted, etc).deleteApplication(id): Removes application logic.
getAdminBlogs(): Gets all blogs regardless of draft state.createBlog(...),updateBlog(...),deleteBlog(...): Mutate blog payloads and scrub input fields through DOMPurify for strict XSS prevention before pushing to Postgres.
- Includes internal mutations for job listings.
getNewsletters(): Fetches the list of all successful subscribers (active state managed).
model User {
id String @id @default(cuid())
name String
email String @unique
role String @default("user") // "user" or "admin"
blogs Blog[]
...
}model Blog {
id String @id @default(cuid())
title String
content String
slug String @unique
status BlogStatus @default(draft)
authorId String
...
}model Job {
id String @id @default(cuid())
status JobStatus @default(draft)
...
}
model Application {
id String @id @default(cuid())
jobId String
status ApplicationStatus @default(pending)
resumeUrl String?
...
}model Newsletter {
id String @id @default(cuid())
email String @unique
isActive Boolean @default(true)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([email])
@@map("newsletter")
}Configuration: lib/auth.ts
export const auth = betterAuth({
database: prismaAdapter(prisma, { provider: "postgresql" }),
emailAndPassword: { enabled: true, minPasswordLength: 8 },
plugins: [bearer()],
cookie: {
sessionToken: {
crossSite: 'lax',
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
},
},
});All admin functions check an isAdmin(user) function against the session token. Unauthorized calls inside app/actions/ immediately throw runtime Next.js errors bounding to an unauthorized UI state.
| Feature | Implementation | Location |
|---|---|---|
| Server Actions | Opaque mutation identifiers | Next.js Engine |
| CSRF Protection | SameSite=Lax cookies | lib/auth.ts |
| XSS Prevention | DOMPurify sanitization | Blog mutations |
| Admin Authorization | isAdmin() checks |
All admin actions |
| Rate Limiting | Upstash Redis (sliding window) | lib/ratelimit.ts |
| File Upload Validation | Magic bytes + size + MIME | app/actions/applications.ts |
| Security Headers | CSP, HSTS, X-Frame-Options | proxy.ts |
| Input Validation | Zod schemas | lib/validators.ts |
| SQL Injection | Prisma ORM mapping | All database queries |
Configuration:
sentry.client.config.ts- Browser errors, performancesentry.server.config.ts- Server-side errors
Tracked natively via components/web-vitals.tsx for metrics like LCP, FID, CLS, FCP, TTFB.
Domain hooks live independently under their related features logic or specific hook folders instead of bloating a global context.
Domain Hooks:
features/dashboard/hooks/use-dashboard-blogs.tsfeatures/dashboard/hooks/use-dashboard-applications.tsfeatures/dashboard/hooks/use-dashboard-careers.tsfeatures/dashboard/hooks/use-dashboard-newsletter.tsfeatures/blogs/hooks/(or related roothooks/depending on context)
Data queries execute their respective generated Server Action seamlessly.
Process in submitApplication Server Action:
- Extract file from
FormDataargument block. - Validate size locally.
- Use magic bytes to identify exact PDF/DOC mime-types via
file-typesafely. - Generates a secure
crypto.randomUUID()file reference to stop path traversal hacking. - Emits to
public/uploads/viafs.
Note: Ready to be migrated to S3 natively via streaming.
Dynamic schema generated natively via Next Route fetching.
Disallows /api/, /dashboard/, /_next/.
Utilizes Next.js Server Components globally for <meta> generation across Home, Applications, and static blogs.
Vitest is configured over vitest.config.ts and runs parallel via GitHub action hooks on commits to main to intercept structural failures, typings, and linting problems in strict mode.
.env.local
DATABASE_URL="postgresql://user:pass@host:5432/beeclean"
AUTH_SECRET="32_length"
NEXTAUTH_URL="http://localhost:3000"
NEXT_PUBLIC_APP_URL="http://localhost:3000"
SENTRY_DSN="key@o123"
NEXT_PUBLIC_SENTRY_DSN="key@"
UPSTASH_REDIS_REST_URL="url"
UPSTASH_REDIS_REST_TOKEN="token"Uses lib/env-validator.ts directly in the Root Layout to crash immediately if booted missing critical tokens.
- β
Database schema applied (
npx prisma db push). - β Environment populated (Vercel).
- β
Build Passes standard strict TS check (
bun run build). - β Next.js Server Actions properly bundled and linked against Upstash instances.
- Ready for Node 20.x Next.js Runtime.