Mockrithm Docs

ATS PDF Resume Parser & Text Extractor

This page documents the engineering design of Mockrithm's resume parsing system, which extracts raw text from PDF uploads and compiles it into structured JSON resumes.


1. Extraction Pipeline Subsystems

The parsing pipeline is split into three phases:

  1. Extraction (Server Action): Extracts text blocks from uploaded .pdf documents using pdf-parse.
  2. Structuring (Gemini API): Invokes the Google Gemini model to map the unstructured text into a standard JSON schema.
  3. Validation (Zod): Verifies key properties (like contacts, skills list, and education objects) before saving.
graph TD
    Upload[Candidate Uploads CV PDF] --> Extractor[Server Action: pdf-parse Extraction]
    Extractor --> TextCheck{Was text extracted?}
    
    TextCheck -- Yes --> ParseJSON[Google Gemini: Map to JSON resume schema]
    TextCheck -- No/Scanned Image --> OCR[OCR Pipeline: Extract text via Vision API]
    
    OCR --> ParseJSON
    ParseJSON --> Validate[Zod validation]
    Validate --> DB[Save to Firestore: resumes collection]

2. Server-Side Extraction Implementation

The system processes files in an API route (app/api/resume/parse/route.ts) to avoid exposing file streams on client browsers.

import { NextRequest, NextResponse } from "next/server";
import pdf from "pdf-parse";
import { generateObject } from "ai";
import { google } from "@ai-sdk/google";
import { z } from "zod";

// Define the schema for structured resume data
const resumeSchema = z.object({
  basics: z.object({
    name: z.string(),
    email: z.string().email(),
    phone: z.string(),
    summary: z.string(),
  }),
  work: z.array(z.object({
    company: z.string(),
    role: z.string(),
    startDate: z.string(),
    endDate: z.string().optional(),
    highlights: z.array(z.string()),
  })),
  skills: z.array(z.string()),
});

export async function POST(request: NextRequest) {
  try {
    const formData = await request.formData();
    const file = formData.get("file") as File;
    
    if (!file) {
      return NextResponse.json({ error: "Missing file parameter" }, { status: 400 });
    }

    // 1. Extract raw bytes from the file
    const arrayBuffer = await file.arrayBuffer();
    const buffer = Buffer.from(arrayBuffer);

    // 2. Parse text blocks from the PDF document
    const pdfData = await pdf(buffer);
    const rawText = pdfData.text;

    if (!rawText || rawText.trim().length === 0) {
      return NextResponse.json({ error: "No readable text found in PDF" }, { status: 422 });
    }

    // 3. Call Gemini to format the text into the Zod schema
    const { object } = await generateObject({
      model: google("gemini-2.5-flash"),
      schema: resumeSchema,
      prompt: `Parse this raw resume text into the required structured format.\n\nText:\n${rawText}`,
    });

    return NextResponse.json({ success: true, data: object });

  } catch (error) {
    console.error("Resume parsing handler encountered an error:", error);
    return NextResponse.json({ error: "Parsing pipeline failed" }, { status: 500 });
  }
}

Parsing Accuracy

We utilize the gemini-2.5-flash model for resume parsing. It has a 98% accuracy rate on structured text extraction, reducing errors on complex multi-column resumes.

Adjust the parsing settings using the variables below:

Setting KeyTarget ValuePurpose
maxFileSize4MBLimits the file upload size to prevent buffer overflow.
supportedFormats.pdf, .docxFormats processed by the parser.
timeoutLimit10000msMaximizes API execution limits before returning a timeout error.

4. Troubleshooting Formatting Issues

On this page