cd /news/ai-tools/building-texfolio-an-ai-powered-late… Β· home β€Ί topics β€Ί ai-tools β€Ί article
[ARTICLE Β· art-25928] src=dev.to pub= topic=ai-tools verified=true sentiment=↑ positive

Building TexFolio: An AI-Powered LaTeX Resume Builder with Hono, LangGraph & BullMQ

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.

read7 min publishedJun 13, 2026

TexFolio is an open-source, AI-powered LaTeX resume builder. Instead of converting HTML to PDF, it compiles real pdflatex

documents 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.

Most 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.

TexFolio takes a different path: it compiles real LaTeX with pdflatex

, 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.

TexFolio is a Turborepo monorepo with clear separation between the React frontend, the Hono API, and infrastructure services.

CLIENT (React 19 + Vite + Tailwind v4 + Zustand + React Query)
        β”‚  HTTPS (Clerk JWT / API Key)
        β–Ό
HONO v4 API SERVER
  Request ID β†’ Logger β†’ CORS + SecHeaders β†’ Tiered Rate Limit β†’ Input Sanitizer
        β”‚
  Routes: /resumes /ai /agents /organizations /payments /me
        β”‚
  Auth (Clerk) β†’ RBAC (requireRole) β†’ API Key (HMAC) β†’ Audit Trail
        β”‚
        β–Ό
MongoDB Atlas β€’ Redis β€’ BullMQ Workers β€’ External APIs
                                  β”‚   (NVIDIA NIM, Gemini, Groq, Clerk, Razorpay, Brevo)
                                  β–Ό
                            pdflatex (Docker or local)

The repo is organized as:

apps/api

β€” Hono v4 backend (services, models, queues, agents, middleware)apps/web

β€” React 19 frontend (features, hooks, stores, pages)apps/latex-renderer

β€” Dedicated Docker container for pdflatex

packages/shared

β€” 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:

requestIdMiddleware

β€” assigns an X-Request-ID

(nanoid) for end-to-end tracingstructuredLogger

β€” JSON logs with correlation IDssecureHeaders()

β€” CSP, X-Frame-Options, nosniff, referrer policycors()

β€” origin whitelist with credentialstieredRateLimiter

β€” Redis-backed limits (Pro: 300/min, Free: 60/min, Anonymous: 20/min)inputSanitizer

β€” XSS and prototype-pollution preventionRoute-level middleware then layers on authMiddleware

(Clerk JWT), requireRole()

(RBAC), and apiKeyMiddleware

(HMAC).

Running pdflatex

on arbitrary user input is a security and reliability minefield. TexFolio solves it in layers.

LaTeX 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%).

{
  concurrency: 2,                          // Max parallel compilations
  limiter: { max: 5, duration: 60000 },    // 5 jobs/min
  defaultJobOptions: {
    attempts: 3,
    backoff: { type: "exponential", delay: 2000 }, // 2s β†’ 4s β†’ 8s
    removeOnComplete: { count: 100 },
    removeOnFail: { count: 50 },
  }
}

The endpoints reflect this: POST /api/resumes/:id/pdf/queue

returns a jobId

, GET .../pdf/queue/:jobId

polls status (waiting β†’ active β†’ completed | failed

), and .../download

returns the binary. A synchronous GET /api/resumes/:id/pdf

also exists for direct compilation.

spawn

, never exec

Command injection is prevented by passing arguments as an array instead of concatenating strings:

// SECURE: no shell interpretation
spawn("docker", ["exec", "texfolio-latex", "pdflatex", "-interaction=nonstopmode", filename]);

// INSECURE (never used): exec(`pdflatex ${filename}`) β€” "; rm -rf /" disaster

All user input is escaped before template rendering (\

, &

, %

, $

, #

, _

, {

, }

, ~

, ^

) to prevent LaTeX injection. Template IDs and filenames are sanitized against path traversal using path.basename()

and regex whitelisting.

SIGKILL

for hung compilations<< >>

delimiters) so templates cannot execute codeTemplates are real .tex

files: classic.tex

, faangpath.tex

, and premium.tex

.

The "Resume Coach" is a LangGraph state machine, not a single prompt. It runs five sequential nodes:

START β†’ content β†’ ats β†’ format β†’ impact β†’ synthesize β†’ END
Node Purpose Weight
content
Content quality analysis 30%
ats
ATS keyword compatibility 25%
format
Structure / layout review 20%
impact
Overall effectiveness 25%
synthesize
Weighted final score + recommendations β€”

Each 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.

You hit it via POST /api/agents/coach

, which returns a finalScore

, per-category analysis, and a recommendations

array. Other AI endpoints include /api/ai/improve

, /api/ai/generate-bullets

, /api/ai/cover-letter

, and /api/agents/import/linkedin

(parses a LinkedIn PDF export into structured resume data).

Reliability comes from a priority-based provider chain wrapped in a circuit breaker:

NVIDIA NIM (Llama 3.1 70B)  β†’  Google Gemini 1.5 Flash  β†’  Groq (Llama 3.1 70B)

The 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

.

TexFolio supports teams with a strict role hierarchy enforced by weight comparison:

Owner (4) β†’ Admin (3) β†’ Editor (2) β†’ Viewer (1)

Organization context is resolved from an X-Organization-Id

header (injected by an Axios interceptor on the frontend) or a route param. The requireRole("admin")

middleware compares role weights and returns 403

when insufficient. Resumes marked visibility: "organization"

are shared across the team, and PDF generation automatically applies the org's locked template, primary color, and font overrides.

Ownership transfer is atomic: promoting a member to owner

automatically demotes the previous owner to admin

, and both changes are written to the audit trail.

<prefix>.<hmac_signature>

, verified with crypto.timingSafeEqual

to prevent timing attacks. Keys are SHA-256 hashed and shown only once.__proto__

/ constructor

/ prototype

, 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

  • PEXPIRE

) that GET /api/me/export

returns a full JSON dump; POST /api/me/delete

performs a soft-delete that redacts PII to [REDACTED]

and anonymizes audit actor IDs over a 30-day buffer.Six MongoDB collections via Mongoose, with compound indexes tuned for real query patterns:

users

β€” Clerk-linked accountsresumes

β€” full resume documents ({ userId: 1, createdAt: -1 }

, plus org-visibility indexes)organizations

β€” branding + settingsorganizationmembers

β€” RBAC roles with a unique { organizationId, userId }

compound indexauditlogs

β€” immutable, TTL-expiring after 90 daysapikeys

β€” HMAC service keys with scopes (read:resumes

, write:resumes

, read:analytics

, admin

)| Component | Platform | |---|---| | Frontend | Vercel (Edge CDN) | | Backend API | Render (Docker) | | Database | MongoDB Atlas (replica set) | | Cache + Queue | Redis Cloud | | LaTeX Renderer | Docker container (debian:bullseye-slim + TeX Live) |

A two-stage GitHub Actions pipeline runs on every push/PR to main

: a Security & Code Quality job (npm ci

, npm audit

, lint, build:deploy

), followed by a Build Verification job that confirms the dist/

artifacts exist.

What makes TexFolio different from other resume builders?

It compiles real LaTeX with pdflatex

instead of converting HTML to PDF, producing consistent, ATS-friendly output, and it scores resumes with a multi-agent LangGraph AI pipeline.

What tech stack does TexFolio use?

React 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

in Docker for rendering.

How does TexFolio handle AI provider outages?

A 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.

Is TexFolio GDPR-compliant?

It provides data export (/api/me/export

) and a right-to-erasure endpoint (/api/me/delete

) that anonymizes PII with a 30-day buffer.

If 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.

── more in #ai-tools 4 stories Β· sorted by recency
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/building-texfolio-an…] indexed:0 read:7min 2026-06-13 Β· β€”