Version: 1.0.0 Last Updated: February 2026 Tech Stack: Next.js 16, TypeScript, Tailwind CSS, Prisma, Gemini AI, Inngest, Polar, Nodemailer.
This project is a Next.js 16 SaaS application using the App Router. It follows a monolithic architecture specifically designed for serverless deployment (Vercel).
- Full-Stack Next.js: Both frontend and backend logic reside in
src/app. - Server Actions: We primarily use Server Actions (
src/actions) for mutations (form submissions, updates) instead of traditional API routes, to ensure type safety and reduce boilerplate. - Event-Driven Background Jobs: Heavy tasks (emails, long-running AI processes) are offloaded to Inngest to avoid server timeouts and improve user experience.
- Service Layer Pattern: Core business logic is encapsulated in
src/lib(e.g.,src/lib/ai,src/lib/email.ts) rather than being scattered in UI components. - Database First: The schema is defined in Prisma, serving as the single source of truth for data models.
The project sources are located in the src/ directory.
src/
├── actions/ # Server Actions (Mutations & Data logic)
│ ├── onboarding.ts # Onboarding form submission
│ ├── roadmap.ts # Roadmap & skill status updates
│ └── ...
├── app/ # Next.js App Router (Pages & API Routes)
│ ├── admin/ # Admin Panel (Protected)
│ ├── api/ # API Routes (Webhooks, Auth, Inngest)
│ ├── dashboard/ # User Dashboard
│ ├── onboarding/ # Onboarding Flow
│ └── layout.tsx # Root layout
├── components/ # React Components
│ ├── admin/ # Admin-specific UI
│ ├── onboarding/ # Onboarding forms
│ └── ui/ # Reusable UI primitives (Shadcn/UI)
├── lib/ # Core Business Logic & Utilities
│ ├── ai/ # Gemini AI Logic
│ ├── email/ # Email functions & templates
│ ├── inngest/ # Background Job Definitions
│ └── prisma.ts # Database Client
├── prisma/ # Database Schema
│ └── schema.prisma # PostgreSQL Schema
└── public/ # Static Assets
Authentication is handled by Auth.js (NextAuth v5). It supports OAuth (Google, GitHub) and manages sessions via JWT.
- Config:
src/auth.config.ts- Defines providers and pages.
- Initialization:
src/auth.ts- Exports
auth,signIn,signOut.
- Exports
- Middleware:
src/middleware.ts- Protects routes (
/dashboard,/admin) and redirects unauthenticated users.
- Protects routes (
- API Route:
src/app/api/auth/[...nextauth]/route.ts(Managed via library exports) - Database Models:
User,Account,Sessioninschema.prisma.
- User clicks "Sign In".
- Redirected to provider (Google/GitHub).
- Callback processed by
src/app/api/auth/.... - Session created in DB + JWT cookie set.
middleware.tsverifies token on navigation.
The onboarding flow collects user preferences to generate a personalized roadmap.
- Page:
src/app/onboarding/page.tsx- Renders the multi-step form.
- Components:
src/components/onboarding/onboarding-flow.tsx- Client-side state for the wizard. Features a new Hexagon layout for career roles.
- API Route:
src/app/api/resume/upload/route.ts(Resume parsing)- Extracts text from PDF/DOCX and passes it to Gemini to extract normalized skills.
- Server Action:
src/actions/onboarding.ts(submitOnboarding)- Validates input with Zod.
- Checks subscription limits.
- Saves
OnboardingProfileto DB. - Triggers AI Generation.
- Database Model:
OnboardingProfile,Resume.
The system maps users to specific Career Roles to determine missing skills and build accurate roadmaps.
- Schema:
CareerRole,RoleSkill,Skillinprisma/schema.prisma. - Core Logic:
src/lib/roadmap/skillGapEngine.ts- Contains
computeMissingSkills(careerGoal, currentSkills). - Fetches the industry-standard skills for a database
CareerRole. - Uses Gemini AI to semantically match the user's
currentSkillsagainst the standard skills, bypassing exact string-matching limitations, to output a clean array of purely missing topics.
- Contains
- Admin Management: Managed via DB seeding arrays (
scripts/seed-roles.ts).
This is the core engine. It uses Google's Gemini 2.5 Flash to generate structured JSON roadmaps.
- Generator:
src/lib/ai/roadmap-generator.ts- Purpose: Main orchestration function
generateRoadmap.
- Purpose: Main orchestration function
- Utils:
src/lib/ai/roadmap-utils.ts- Purpose: Input normalization and hash generation.
- Trigger: Called from
src/actions/onboarding.ts.
- User Submits:
submitOnboardingcallsgenerateRoadmap. - Normalization & Gap Analysis: Inputs are normalized, and
computeMissingSkillscalculates exact gaps. - Cache Check: Code hashes the logic and checks
RoadmapTemplateforinputsHash.- Hit: Clones existing template.
- Miss: Calls Gemini API.
- Prompt Construction:
src/lib/ai/roadmap-generator.tsbuilds a prompt usingroadmap_system_promptfromGlobalSettings(defaults to a robust fallback if DB is empty). The prompt dictates unlimited flexible phases. - Gemini Call: Requests JSON output tailored purely to the missing skills.
- Parsing: Validates response with
Zodschemas. - YouTube Enrichment: For each skill, the system searches YouTube API (or uses oEmbed) to find real video links.
- Persist: Saves
RoadmapTemplate->TemplatePhase->TemplateSkill. - Instantiation: Creates a user-specific
Roadmaplinked to the template.
To save AI costs and speed up generation, identical inputs reuse generated roadmaps.
- Hash Logic:
src/lib/ai/roadmap-utils.ts(generateTemplateHash) - Schema:
RoadmapTemplatetable (inputsHashunique index). - Implementation:
src/lib/ai/roadmap-generator.ts(Lines 63-81).
- Inputs are sorted and lowercased.
- SHA-256 hash (or similar) is generated.
- DB lookup:
prisma.roadmapTemplate.findUnique({ where: { inputsHash } }).
The AI suggests search terms, and the system resolves them to actual playable URLs.
- Search Logic:
src/lib/ai/roadmap-generator.ts(searchYouTubefunction). - Schema:
TemplateResource(stores Title, URL, Thumbnail). - Whitelist/Blacklist:
YouTubeChannelmodel (in schema).
- AI returns:
{"title": "Learn React", "channel": "Fireship"} searchYouTubecalls Google YouTube Data API.- Fallback: If API fails/limit reached, tries oEmbed or generates a search result URL.
- Thumbnail and Video ID are stored in
TemplateResource.
The user hub for viewing active roadmaps and tracking progress.
- Page:
src/app/dashboard/page.tsx - Action:
src/actions/roadmap.ts(toggleSkillStatus)- Updates
SkillProgressstatus. - Logs entry in
ProgressLog.
- Updates
- Database:
SkillProgress(linksRoadmapandTemplateSkill).
We use Inngest for reliable event-driven task execution (e.g., sending emails asynchronously).
- Client:
src/lib/inngest/client.ts - Functions:
src/lib/inngest/functions/welcome-email.ts: Sends welcome email on signup.roadmap-email.ts: Sends email when roadmap is ready.
- API Route:
src/app/api/inngest/route.ts(Entry point for Inngest executor).
- Code calls
inngest.send({ name: "roadmap/generated", data: ... })(e.g., inroadmap-generator.ts). - Inngest receives event.
- Inngest calls back
src/app/api/inngest. functions/roadmap-email.tsexecutes and sends the email.
Transactional emails are sent via SMTP (Nodemailer).
- Service:
src/lib/email.ts(sendEmailfunction). - Templates:
src/lib/email-templates.ts(or individual ts files inlib/email). - Trigger: Usually via Inngest functions.
Inngest Function -> lib/email.ts -> SMTP Server -> User Inbox.
Restricted area for managing app content.
- Root:
src/app/admin - Layout:
src/app/admin/layout.tsx- Protection: Checks
session.user.role === "ADMIN".
- Protection: Checks
- Middleware:
src/middleware.ts(Double protection). - Features:
/admin/users: User management./admin/career-roles: Manage career paths./admin/email-templates: Edit email content.
Handles billing and premium features using Polar.
- Webhook Handler:
src/app/api/webhooks/polar/route.ts- Purpose: Listens for
checkout.succeededandsubscription.*events. - Updates
Subscriptionmodel in DB.
- Purpose: Listens for
- Service:
src/lib/polar.ts. - Database:
Subscription,PolarWebhookEvent.
- User pays on Polar.
- Polar sends webhook to
/api/webhooks/polar. - Route verifies and updates user status to
PREMIUM. submitOnboardingchecksSubscriptionstatus before allowing generation.
The prisma/schema.prisma file defines our data structure.
- User / Account: Identity.
- RoadmapTemplate / TemplatePhase / TemplateSkill: The shared curriculum definitions (Cached).
- Roadmap / SkillProgress: The user instance of the curriculum and their personal progress.
- CareerRole / Skill / RoleSkill: The normalized career skills database utilized by the skill gap engine.
- Resume: Stores user uploads, raw text extraction, and normalized skills array payload.
- GlobalSettings: Dynamic site configuration, such as storing the primary Gemini System Prompts.
Create a .env file with the following:
- Database:
DATABASE_URL(Postgres connection string). - Auth:
AUTH_SECRET,AUTH_GOOGLE_ID,AUTH_GOOGLE_SECRET,AUTH_GITHUB_ID,AUTH_GITHUB_SECRET. - AI:
GEMINI_API_KEY,YOUTUBE_API_KEY. - Email:
EMAIL_SERVER_HOST,EMAIL_SERVER_PORT,EMAIL_SERVER_USER,EMAIL_SERVER_PASSWORD,EMAIL_FROM. - Services:
INNGEST_SIGNING_KEY,INNGEST_EVENT_KEY,POLAR_ACCESS_TOKEN.
Scenario: User Signs Up and Generates a Roadmap
- User logs in via Google (Auth.js).
- User lands on
/dashboard, redirected to/onboarding(Middleware). - User fills form -> Clicks "Generate".
- Frontend calls Server Action
submitOnboarding. - Backend checks
RoadmapTemplatecache.- If miss: Calls Gemini AI, parses JSON, fetches YouTube links, saves new Template to DB.
- Backend creates
Roadmapinstance for User. - Backend emits
roadmap/generatedevent to Inngest. - Inngest triggers email function -> generic "Your roadmap is ready" email sent via Nodemailer.
- Frontend receives
roadmapIdand redirects user to/dashboard/roadmap/[id]. - User sees roadmap, checks off a skill ->
toggleSkillStatusupdatesSkillProgressin DB.