{"slug": "building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq", "title": "Building TexFolio: An AI-Powered LaTeX Resume Builder with Hono, LangGraph & BullMQ", "summary": "TexFolio, an open-source AI-powered LaTeX resume builder, compiles real pdflatex documents in the background to produce typography-perfect, ATS-friendly resumes. The backend runs on Hono v4 with MongoDB Atlas, Redis, BullMQ for async PDF jobs, Clerk auth, and role-based access control, while a multi-agent LangGraph pipeline scores resumes across Content, ATS, Format, and Impact.", "body_md": "**TexFolio** is an open-source, AI-powered LaTeX resume builder. Instead of converting HTML to PDF, it compiles real `pdflatex`\n\ndocuments in the background to produce typography-perfect, ATS-friendly resumes. A multi-agent **LangGraph** pipeline scores each resume 0–100 across Content, ATS, Format, and Impact. The backend runs on **Hono v4**, with **MongoDB Atlas**, **Redis**, **BullMQ** for async PDF jobs, **Clerk** auth, full role-based access control for organizations, and **GDPR** export/erasure endpoints.\n\nMost resume builders generate HTML and convert it to PDF. The result is inconsistent spacing, fragile layouts, and output that Applicant Tracking Systems (ATS) struggle to parse.\n\nTexFolio takes a different path: it compiles **real LaTeX** with `pdflatex`\n\n, the typesetting standard used in academia and publishing. The output is consistent, clean, and ATS-friendly by design. The hard part is running LaTeX compilation safely, at scale, inside a web app, and that is where most of the engineering lives.\n\nTexFolio is a **Turborepo monorepo** with clear separation between the React frontend, the Hono API, and infrastructure services.\n\n```\nCLIENT (React 19 + Vite + Tailwind v4 + Zustand + React Query)\n        │  HTTPS (Clerk JWT / API Key)\n        ▼\nHONO v4 API SERVER\n  Request ID → Logger → CORS + SecHeaders → Tiered Rate Limit → Input Sanitizer\n        │\n  Routes: /resumes /ai /agents /organizations /payments /me\n        │\n  Auth (Clerk) → RBAC (requireRole) → API Key (HMAC) → Audit Trail\n        │\n        ▼\nMongoDB Atlas • Redis • BullMQ Workers • External APIs\n                                  │   (NVIDIA NIM, Gemini, Groq, Clerk, Razorpay, Brevo)\n                                  ▼\n                            pdflatex (Docker or local)\n```\n\nThe repo is organized as:\n\n`apps/api`\n\n— Hono v4 backend (services, models, queues, agents, middleware)`apps/web`\n\n— React 19 frontend (features, hooks, stores, pages)`apps/latex-renderer`\n\n— Dedicated Docker container for `pdflatex`\n\n`packages/shared`\n\n— Zod schemas and TypeScript types as a single source of truthThe API runs on **Hono v4**, a Web Standards framework chosen over Express and Fastify for speed and a middleware-first design. Every request flows through a strict, ordered pipeline:\n\n`requestIdMiddleware`\n\n— assigns an `X-Request-ID`\n\n(nanoid) for end-to-end tracing`structuredLogger`\n\n— JSON logs with correlation IDs`secureHeaders()`\n\n— CSP, X-Frame-Options, nosniff, referrer policy`cors()`\n\n— origin whitelist with credentials`tieredRateLimiter`\n\n— Redis-backed limits (Pro: 300/min, Free: 60/min, Anonymous: 20/min)`inputSanitizer`\n\n— XSS and prototype-pollution preventionRoute-level middleware then layers on `authMiddleware`\n\n(Clerk JWT), `requireRole()`\n\n(RBAC), and `apiKeyMiddleware`\n\n(HMAC).\n\nRunning `pdflatex`\n\non arbitrary user input is a security and reliability minefield. TexFolio solves it in layers.\n\nLaTeX compilation is offloaded from the HTTP thread into a **BullMQ** queue backed by Redis. The client enqueues a job and polls for progress (10% → 30% → 100%).\n\n```\n{\n  concurrency: 2,                          // Max parallel compilations\n  limiter: { max: 5, duration: 60000 },    // 5 jobs/min\n  defaultJobOptions: {\n    attempts: 3,\n    backoff: { type: \"exponential\", delay: 2000 }, // 2s → 4s → 8s\n    removeOnComplete: { count: 100 },\n    removeOnFail: { count: 50 },\n  }\n}\n```\n\nThe endpoints reflect this: `POST /api/resumes/:id/pdf/queue`\n\nreturns a `jobId`\n\n, `GET .../pdf/queue/:jobId`\n\npolls status (`waiting → active → completed | failed`\n\n), and `.../download`\n\nreturns the binary. A synchronous `GET /api/resumes/:id/pdf`\n\nalso exists for direct compilation.\n\n`spawn`\n\n, never `exec`\n\nCommand injection is prevented by passing arguments as an array instead of concatenating strings:\n\n```\n// SECURE: no shell interpretation\nspawn(\"docker\", [\"exec\", \"texfolio-latex\", \"pdflatex\", \"-interaction=nonstopmode\", filename]);\n\n// INSECURE (never used): exec(`pdflatex ${filename}`) — \"; rm -rf /\" disaster\n```\n\nAll user input is escaped before template rendering (`\\`\n\n, `&`\n\n, `%`\n\n, `$`\n\n, `#`\n\n, `_`\n\n, `{`\n\n, `}`\n\n, `~`\n\n, `^`\n\n) to prevent LaTeX injection. Template IDs and filenames are sanitized against path traversal using `path.basename()`\n\nand regex whitelisting.\n\n`SIGKILL`\n\nfor hung compilations`<< >>`\n\ndelimiters) so templates cannot execute codeTemplates are real `.tex`\n\nfiles: `classic.tex`\n\n, `faangpath.tex`\n\n, and `premium.tex`\n\n.\n\nThe \"Resume Coach\" is a **LangGraph** state machine, not a single prompt. It runs five sequential nodes:\n\n```\nSTART → content → ats → format → impact → synthesize → END\n```\n\n| Node | Purpose | Weight |\n|---|---|---|\n`content` |\nContent quality analysis | 30% |\n`ats` |\nATS keyword compatibility | 25% |\n`format` |\nStructure / layout review | 20% |\n`impact` |\nOverall effectiveness | 25% |\n`synthesize` |\nWeighted final score + recommendations | — |\n\nEach node creates an LLM instance, sends the resume data, requests JSON-only output, and returns a partial state update. Critically, **the pipeline never halts on a single failure**: if a node cannot parse the LLM output, it returns a zero score and moves on.\n\nYou hit it via `POST /api/agents/coach`\n\n, which returns a `finalScore`\n\n, per-category analysis, and a `recommendations`\n\narray. Other AI endpoints include `/api/ai/improve`\n\n, `/api/ai/generate-bullets`\n\n, `/api/ai/cover-letter`\n\n, and `/api/agents/import/linkedin`\n\n(parses a LinkedIn PDF export into structured resume data).\n\nReliability comes from a priority-based provider chain wrapped in a circuit breaker:\n\n```\nNVIDIA NIM (Llama 3.1 70B)  →  Google Gemini 1.5 Flash  →  Groq (Llama 3.1 70B)\n```\n\nThe **circuit breaker** (CLOSED → OPEN → HALF_OPEN) trips after 5 consecutive failures, waits 30 seconds, then tests recovery with 2 successes before closing. Its live state is exposed at `GET /health/ai`\n\n.\n\nTexFolio supports teams with a strict role hierarchy enforced by weight comparison:\n\n```\nOwner (4) → Admin (3) → Editor (2) → Viewer (1)\n```\n\nOrganization context is resolved from an `X-Organization-Id`\n\nheader (injected by an Axios interceptor on the frontend) or a route param. The `requireRole(\"admin\")`\n\nmiddleware compares role weights and returns `403`\n\nwhen insufficient. Resumes marked `visibility: \"organization\"`\n\nare shared across the team, and PDF generation automatically applies the org's locked template, primary color, and font overrides.\n\nOwnership transfer is atomic: promoting a member to `owner`\n\nautomatically demotes the previous owner to `admin`\n\n, and both changes are written to the audit trail.\n\n`<prefix>.<hmac_signature>`\n\n, verified with `crypto.timingSafeEqual`\n\nto prevent timing attacks. Keys are SHA-256 hashed and shown only once.`__proto__`\n\n/ `constructor`\n\n/ `prototype`\n\n, null bytes, and control characters; caps strings at 10,000 chars to prevent ReDoS. Webhook routes skip sanitization to preserve the raw body for signature verification.`INCR`\n\n+ `PEXPIRE`\n\n) that `GET /api/me/export`\n\nreturns a full JSON dump; `POST /api/me/delete`\n\nperforms a soft-delete that redacts PII to `[REDACTED]`\n\nand anonymizes audit actor IDs over a 30-day buffer.Six MongoDB collections via Mongoose, with compound indexes tuned for real query patterns:\n\n`users`\n\n— Clerk-linked accounts`resumes`\n\n— full resume documents (`{ userId: 1, createdAt: -1 }`\n\n, plus org-visibility indexes)`organizations`\n\n— branding + settings`organizationmembers`\n\n— RBAC roles with a unique `{ organizationId, userId }`\n\ncompound index`auditlogs`\n\n— immutable, TTL-expiring after 90 days`apikeys`\n\n— HMAC service keys with scopes (`read:resumes`\n\n, `write:resumes`\n\n, `read:analytics`\n\n, `admin`\n\n)| Component | Platform |\n|---|---|\n| Frontend | Vercel (Edge CDN) |\n| Backend API | Render (Docker) |\n| Database | MongoDB Atlas (replica set) |\n| Cache + Queue | Redis Cloud |\n| LaTeX Renderer | Docker container (`debian:bullseye-slim` + TeX Live) |\n\nA two-stage GitHub Actions pipeline runs on every push/PR to `main`\n\n: a Security & Code Quality job (`npm ci`\n\n, `npm audit`\n\n, lint, `build:deploy`\n\n), followed by a Build Verification job that confirms the `dist/`\n\nartifacts exist.\n\n**What makes TexFolio different from other resume builders?**\n\nIt compiles real LaTeX with `pdflatex`\n\ninstead of converting HTML to PDF, producing consistent, ATS-friendly output, and it scores resumes with a multi-agent LangGraph AI pipeline.\n\n**What tech stack does TexFolio use?**\n\nReact 19 and Vite on the frontend; Hono v4, MongoDB, Redis, and BullMQ on the backend; LangGraph with NVIDIA NIM, Google Gemini, and Groq for AI; and `pdflatex`\n\nin Docker for rendering.\n\n**How does TexFolio handle AI provider outages?**\n\nA circuit breaker wraps the LLM calls, and a failover chain (NVIDIA NIM → Gemini → Groq) provides resilience. Individual pipeline nodes degrade gracefully rather than failing the whole request.\n\n**Is TexFolio GDPR-compliant?**\n\nIt provides data export (`/api/me/export`\n\n) and a right-to-erasure endpoint (`/api/me/delete`\n\n) that anonymizes PII with a 30-day buffer.\n\nIf you're building AI into a SaaS, the patterns here (queue-based heavy work, circuit-breaker-wrapped LLMs, fail-open rate limiting, and a shared validation schema) transfer to almost any stack.", "url": "https://wpnews.pro/news/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq", "canonical_source": "https://dev.to/theunstopabble/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq-1p0k", "published_at": "2026-06-13 05:49:15+00:00", "updated_at": "2026-06-13 06:17:15.228374+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-agents"], "entities": ["TexFolio", "Hono", "MongoDB Atlas", "Redis", "BullMQ", "Clerk", "LangGraph", "NVIDIA NIM"], "alternates": {"html": "https://wpnews.pro/news/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq", "markdown": "https://wpnews.pro/news/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq.md", "text": "https://wpnews.pro/news/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq.txt", "jsonld": "https://wpnews.pro/news/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq.jsonld"}}