Backend Architecture
Relevant source files
The following files were used as context for generating this wiki page:The backend for the GUC Resume Enhancer is built using Next.js API Routes, providing a serverless architecture for handling application logic. It integrates with Supabase for both the PostgreSQL database and file storage, uses Prisma as the Object-Relational Mapper (ORM), and relies on NextAuth for authentication and session management.
The core responsibilities of the backend include managing user accounts, processing resume uploads (PDF and DOCX), orchestrating AI-powered enhancements via third-party LLM providers, and serving original and enhanced resume files. The system is designed with a clear separation of concerns, with dedicated API routes for each primary function.
Core Technologies
The backend is built upon a modern TypeScript stack, leveraging serverless functions and managed services.
| Technology | Purpose | Configuration/Source |
|---|---|---|
| Next.js API Routes | Provides the serverless function framework for all backend endpoints. | api/**/*.ts |
| Prisma | ORM for database access, schema management, and type-safe queries. | schema.prisma |
| PostgreSQL (Supabase) | The primary relational database for storing all application data. | SUPABASE_SETUP.md |
| Supabase Storage | Used for storing uploaded resume files (PDF, DOCX). | SUPABASE_SETUP.md |
| NextAuth.js | Handles user authentication, session management, and authorization. | admin/resumes/page.tsx:8-12 |
Database Schema
The data model is defined in schema.prisma and managed by Prisma. It consists of several related tables to store user information, resumes, enhancements, and system data.
Sources: schema.prisma
Entity Relationship Diagram
This diagram illustrates the relationships between the main entities in the database.
Key Data Models
Resume Model
Stores metadata and extracted text content for each uploaded resume.
| Field | Type | Description |
|---|---|---|
id | String | Unique identifier (CUID). |
userId | String | Foreign key linking to the User model. |
originalName | String | The original filename of the uploaded resume. |
cloudStoragePath | String | The path to the file in Supabase Storage. |
fileType | String | The type of the file, e.g., 'pdf' or 'docx'. |
originalContent | String | The raw text extracted from the uploaded file. |
status | String | The current status of the resume (e.g., "uploaded", "processing"). |
Sources: schema.prisma:73-84
ResumeEnhancement Model
Stores the result of an AI enhancement for a specific resume.
| Field | Type | Description |
|---|---|---|
id | String | Unique identifier (CUID). |
resumeId | String | Foreign key linking to the Resume model. |
enhancementType | String | The type of enhancement applied (e.g., 'skills_certifications'). |
enhancedContent | String | The AI-generated enhanced text content in Markdown format. |
llmProvider | String | The LLM provider used (e.g., 'openai', 'anthropic', 'gemini'). |
status | String | The status of the enhancement job (e.g., "processing", "completed", "error"). |
Sources: schema.prisma:86-98
UserApiKey Model
Stores user-provided API keys for LLM providers in an encrypted format.
| Field | Type | Description |
|---|---|---|
id | String | Unique identifier (CUID). |
userId | String | Foreign key linking to the User model. |
provider | String | The LLM provider ('openai', 'anthropic', 'gemini'). |
encryptedKey | String | The encrypted API key. |
defaultModel | String | The default model to use for this provider (e.g., 'gpt-4o-mini'). |
isActive | Boolean | Whether this key is the currently active one for the user. |
Sources: schema.prisma:100-112
Authentication and Authorization
Authentication is handled by NextAuth.js. API routes are protected by checking for a valid server-side session.
Sources: api/resumes/upload/route.ts:50-52, admin/resumes/page.tsx:8-16
// Example of session check in an API route
const session = await getServerSession(authOptions);
if (!session?.user?.id) {
return NextResponse.json({ message: "Unauthorized" }, { status: 401 });
}
Sources: api/resumes/upload/route.ts:50-54
The system also supports role-based access control. For example, the admin page checks if the user has the "admin" role before rendering content.
// Example of role check in a page component
const userRole = (session.user as { role?: string }).role;
if (userRole !== "admin") {
redirect("/dashboard");
}
Sources: admin/resumes/page.tsx:13-16
API Endpoints and Flows
The backend exposes several API endpoints to handle the application's core functionality.
Resume Upload Flow
The POST /api/resumes/upload endpoint manages the entire process of uploading a new resume.
Process:
- Authentication: Verifies the user's session.
- File Validation: Checks the incoming file's type (PDF/DOCX) and size (max 10MB).
- Text Extraction: Converts the file to a buffer and uses
pdf-parseormammothto extract raw text content. - File Storage: Uploads the original file to a user-specific folder in the Supabase
resumesbucket. - Database Entry: Creates a new record in the
Resumetable with metadata and the extracted text.
Sources: api/resumes/upload/route.ts
The following sequence diagram illustrates the resume upload process.
Resume Enhancement Flow
The POST /api/enhance-resume/[id] endpoint triggers the AI enhancement process for a specific resume.
Process:
- Authentication: Verifies the user's session.
- Data Fetching: Retrieves the resume content and the user's active, encrypted API key from the database.
- Decryption: Decrypts the API key for use.
- LLM Call: Based on the
providerassociated with the key, it calls the appropriate LLM API (OpenAI, Anthropic, or Gemini) with a tailored prompt. - Database Update: Creates a
ResumeEnhancementrecord withstatus: 'processing', then updates it tostatus: 'completed'with the AI-generated content upon success, orstatus: 'error'on failure.
Sources: api/enhance-resume/[id]/route.ts
The sequence diagram below shows the enhancement flow.
File Serving and Downloading
The backend provides two distinct ways to access resume files: a proxy for viewing and a generator for downloading.
This flowchart shows the logic for file retrieval.
- PDF Proxy (
GET /api/proxy-pdf/[id]): This endpoint is used for displaying the original PDF in the browser. It fetches the file directly from Supabase Storage and streams it to the client with theContent-Type: application/pdfandContent-Disposition: inlineheaders. This avoids CORS issues that would arise from linking directly to the storage URL on the client side.
Sources: api/proxy-pdf/[id]/route.ts - PDF Download (
GET /api/download-resume/[id]): This endpoint generates a new PDF document from the enhanced text content stored in the database. It uses thejspdflibrary to construct the PDF on the fly and returns it with aContent-Disposition: attachmentheader to trigger a file download.
Sources: api/download-resume/[id]/route.ts
Storage Architecture
The application leverages Supabase for both database and file storage, simplifying infrastructure management.
Database
The primary database is a PostgreSQL instance managed by Supabase. All interactions are funneled through the Prisma client, ensuring type safety and abstracting away raw SQL. The connection string is configured via the DATABASE_URL environment variable.
Sources: SUPABASE_SETUP.md, schema.prisma:5-8
File Storage
Uploaded resumes are stored in Supabase Storage.
- Bucket: A private bucket named
resumesis used. - File Structure: Files are organized by user ID to ensure data isolation:
resumes/{userId}/{timestamp}-resume.pdf. - Security: The bucket is kept private. Access is controlled by Row Level Security (RLS) policies on the
storage.objectstable. These policies ensure that authenticated users can onlyINSERT,SELECT, andDELETEfiles within their own designated folder (whereauth.uid()::text = (storage.foldername(name))[1]).
Sources: SUPABASE_SETUP.md, INTEGRATION_GUIDE.md