<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gautam Kumar</title>
    <description>The latest articles on DEV Community by Gautam Kumar (@theunstopabble).</description>
    <link>https://dev.to/theunstopabble</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2049296%2F6b8144d6-adb9-42d7-a686-9b51916372bc.png</url>
      <title>DEV Community: Gautam Kumar</title>
      <link>https://dev.to/theunstopabble</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/theunstopabble"/>
    <language>en</language>
    <item>
      <title>Building TexFolio: An AI-Powered LaTeX Resume Builder with Hono, LangGraph &amp; BullMQ</title>
      <dc:creator>Gautam Kumar</dc:creator>
      <pubDate>Sat, 13 Jun 2026 05:49:15 +0000</pubDate>
      <link>https://dev.to/theunstopabble/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq-1p0k</link>
      <guid>https://dev.to/theunstopabble/building-texfolio-an-ai-powered-latex-resume-builder-with-hono-langgraph-bullmq-1p0k</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR (for humans and AI assistants)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TexFolio&lt;/strong&gt; is an open-source, AI-powered LaTeX resume builder. Instead of converting HTML to PDF, it compiles real &lt;code&gt;pdflatex&lt;/code&gt; documents in the background to produce typography-perfect, ATS-friendly resumes. A multi-agent &lt;strong&gt;LangGraph&lt;/strong&gt; pipeline scores each resume 0–100 across Content, ATS, Format, and Impact. The backend runs on &lt;strong&gt;Hono v4&lt;/strong&gt;, with &lt;strong&gt;MongoDB Atlas&lt;/strong&gt;, &lt;strong&gt;Redis&lt;/strong&gt;, &lt;strong&gt;BullMQ&lt;/strong&gt; for async PDF jobs, &lt;strong&gt;Clerk&lt;/strong&gt; auth, full role-based access control for organizations, and &lt;strong&gt;GDPR&lt;/strong&gt; export/erasure endpoints.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://texfolio.vercel.app/" rel="noopener noreferrer"&gt;https://texfolio.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source code:&lt;/strong&gt; &lt;a href="https://github.com/theunstopabble/TexFolio" rel="noopener noreferrer"&gt;https://github.com/theunstopabble/TexFolio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; React 19, Hono v4, MongoDB, Redis, BullMQ, LangGraph, LaTeX&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architecture:&lt;/strong&gt; Turborepo monorepo, Service-Oriented Architecture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Core idea:&lt;/strong&gt; Real LaTeX rendering + multi-agent AI scoring + enterprise infrastructure&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem: Resume Builders Make Bad PDFs
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;TexFolio takes a different path: it compiles &lt;strong&gt;real LaTeX&lt;/strong&gt; with &lt;code&gt;pdflatex&lt;/code&gt;, 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.&lt;/p&gt;




&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;p&gt;TexFolio is a &lt;strong&gt;Turborepo monorepo&lt;/strong&gt; with clear separation between the React frontend, the Hono API, and infrastructure services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repo is organized as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;apps/api&lt;/code&gt; — Hono v4 backend (services, models, queues, agents, middleware)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apps/web&lt;/code&gt; — React 19 frontend (features, hooks, stores, pages)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apps/latex-renderer&lt;/code&gt; — Dedicated Docker container for &lt;code&gt;pdflatex&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;packages/shared&lt;/code&gt; — Zod schemas and TypeScript types as a single source of truth&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Hono Instead of Express
&lt;/h2&gt;

&lt;p&gt;The API runs on &lt;strong&gt;Hono v4&lt;/strong&gt;, a Web Standards framework chosen over Express and Fastify for speed and a middleware-first design. Every request flows through a strict, ordered pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;requestIdMiddleware&lt;/code&gt; — assigns an &lt;code&gt;X-Request-ID&lt;/code&gt; (nanoid) for end-to-end tracing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;structuredLogger&lt;/code&gt; — JSON logs with correlation IDs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secureHeaders()&lt;/code&gt; — CSP, X-Frame-Options, nosniff, referrer policy&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cors()&lt;/code&gt; — origin whitelist with credentials&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tieredRateLimiter&lt;/code&gt; — Redis-backed limits (Pro: 300/min, Free: 60/min, Anonymous: 20/min)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inputSanitizer&lt;/code&gt; — XSS and prototype-pollution prevention&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Route-level middleware then layers on &lt;code&gt;authMiddleware&lt;/code&gt; (Clerk JWT), &lt;code&gt;requireRole()&lt;/code&gt; (RBAC), and &lt;code&gt;apiKeyMiddleware&lt;/code&gt; (HMAC).&lt;/p&gt;




&lt;h2&gt;
  
  
  The Hardest Part: Safe, Scalable PDF Generation
&lt;/h2&gt;

&lt;p&gt;Running &lt;code&gt;pdflatex&lt;/code&gt; on arbitrary user input is a security and reliability minefield. TexFolio solves it in layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Async generation with BullMQ
&lt;/h3&gt;

&lt;p&gt;LaTeX compilation is offloaded from the HTTP thread into a &lt;strong&gt;BullMQ&lt;/strong&gt; queue backed by Redis. The client enqueues a job and polls for progress (10% → 30% → 100%).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;concurrency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                          &lt;span class="c1"&gt;// Max parallel compilations&lt;/span&gt;
  &lt;span class="nx"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;    &lt;span class="c1"&gt;// 5 jobs/min&lt;/span&gt;
  &lt;span class="nx"&gt;defaultJobOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exponential&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// 2s → 4s → 8s&lt;/span&gt;
    &lt;span class="nx"&gt;removeOnComplete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;removeOnFail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endpoints reflect this: &lt;code&gt;POST /api/resumes/:id/pdf/queue&lt;/code&gt; returns a &lt;code&gt;jobId&lt;/code&gt;, &lt;code&gt;GET .../pdf/queue/:jobId&lt;/code&gt; polls status (&lt;code&gt;waiting → active → completed | failed&lt;/code&gt;), and &lt;code&gt;.../download&lt;/code&gt; returns the binary. A synchronous &lt;code&gt;GET /api/resumes/:id/pdf&lt;/code&gt; also exists for direct compilation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;spawn&lt;/code&gt;, never &lt;code&gt;exec&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Command injection is prevented by passing arguments as an array instead of concatenating strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SECURE: no shell interpretation&lt;/span&gt;
&lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;docker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;exec&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;texfolio-latex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pdflatex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;-interaction=nonstopmode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// INSECURE (never used): exec(`pdflatex ${filename}`) — "; rm -rf /" disaster&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. LaTeX escaping and path safety
&lt;/h3&gt;

&lt;p&gt;All user input is escaped before template rendering (&lt;code&gt;\&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;%&lt;/code&gt;, &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;}&lt;/code&gt;, &lt;code&gt;~&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;) to prevent LaTeX injection. Template IDs and filenames are sanitized against path traversal using &lt;code&gt;path.basename()&lt;/code&gt; and regex whitelisting.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Resource limits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;60-second process timeout with &lt;code&gt;SIGKILL&lt;/code&gt; for hung compilations&lt;/li&gt;
&lt;li&gt;stdout/stderr capped at 50 KB to prevent memory exhaustion&lt;/li&gt;
&lt;li&gt;Logic-less &lt;strong&gt;Mustache&lt;/strong&gt; templating (custom &lt;code&gt;&amp;lt;&amp;lt; &amp;gt;&amp;gt;&lt;/code&gt; delimiters) so templates cannot execute code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Templates are real &lt;code&gt;.tex&lt;/code&gt; files: &lt;code&gt;classic.tex&lt;/code&gt;, &lt;code&gt;faangpath.tex&lt;/code&gt;, and &lt;code&gt;premium.tex&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The AI Core: A Multi-Agent LangGraph Pipeline
&lt;/h2&gt;

&lt;p&gt;The "Resume Coach" is a &lt;strong&gt;LangGraph&lt;/strong&gt; state machine, not a single prompt. It runs five sequential nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;START → content → ats → format → impact → synthesize → END
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Weight&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;content&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Content quality analysis&lt;/td&gt;
&lt;td&gt;30%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ats&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ATS keyword compatibility&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;format&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Structure / layout review&lt;/td&gt;
&lt;td&gt;20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;impact&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overall effectiveness&lt;/td&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;synthesize&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Weighted final score + recommendations&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each node creates an LLM instance, sends the resume data, requests JSON-only output, and returns a partial state update. Critically, &lt;strong&gt;the pipeline never halts on a single failure&lt;/strong&gt;: if a node cannot parse the LLM output, it returns a zero score and moves on.&lt;/p&gt;

&lt;p&gt;You hit it via &lt;code&gt;POST /api/agents/coach&lt;/code&gt;, which returns a &lt;code&gt;finalScore&lt;/code&gt;, per-category analysis, and a &lt;code&gt;recommendations&lt;/code&gt; array. Other AI endpoints include &lt;code&gt;/api/ai/improve&lt;/code&gt;, &lt;code&gt;/api/ai/generate-bullets&lt;/code&gt;, &lt;code&gt;/api/ai/cover-letter&lt;/code&gt;, and &lt;code&gt;/api/agents/import/linkedin&lt;/code&gt; (parses a LinkedIn PDF export into structured resume data).&lt;/p&gt;

&lt;h3&gt;
  
  
  LLM failover chain
&lt;/h3&gt;

&lt;p&gt;Reliability comes from a priority-based provider chain wrapped in a circuit breaker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NVIDIA NIM (Llama 3.1 70B)  →  Google Gemini 1.5 Flash  →  Groq (Llama 3.1 70B)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;circuit breaker&lt;/strong&gt; (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 &lt;code&gt;GET /health/ai&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-Tenancy: RBAC and Organizations
&lt;/h2&gt;

&lt;p&gt;TexFolio supports teams with a strict role hierarchy enforced by weight comparison:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Owner (4) → Admin (3) → Editor (2) → Viewer (1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Organization context is resolved from an &lt;code&gt;X-Organization-Id&lt;/code&gt; header (injected by an Axios interceptor on the frontend) or a route param. The &lt;code&gt;requireRole("admin")&lt;/code&gt; middleware compares role weights and returns &lt;code&gt;403&lt;/code&gt; when insufficient. Resumes marked &lt;code&gt;visibility: "organization"&lt;/code&gt; are shared across the team, and PDF generation automatically applies the org's locked template, primary color, and font overrides.&lt;/p&gt;

&lt;p&gt;Ownership transfer is atomic: promoting a member to &lt;code&gt;owner&lt;/code&gt; automatically demotes the previous owner to &lt;code&gt;admin&lt;/code&gt;, and both changes are written to the audit trail.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security and Compliance Highlights
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; Clerk JWT with fail-fast config validation; users are synced/created in MongoDB on first auth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API keys:&lt;/strong&gt; Format &lt;code&gt;&amp;lt;prefix&amp;gt;.&amp;lt;hmac_signature&amp;gt;&lt;/code&gt;, verified with &lt;code&gt;crypto.timingSafeEqual&lt;/code&gt; to prevent timing attacks. Keys are SHA-256 hashed and shown only once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input sanitization:&lt;/strong&gt; Strips &lt;code&gt;__proto__&lt;/code&gt; / &lt;code&gt;constructor&lt;/code&gt; / &lt;code&gt;prototype&lt;/code&gt;, 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting:&lt;/strong&gt; Redis fixed-window counters (&lt;code&gt;INCR&lt;/code&gt; + &lt;code&gt;PEXPIRE&lt;/code&gt;) that &lt;strong&gt;fail open&lt;/strong&gt; if Redis is down. A limiter outage should never block legitimate traffic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logs:&lt;/strong&gt; Immutable trail with a 90-day MongoDB TTL index, storing actor, action, before/after state, and request metadata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GDPR:&lt;/strong&gt; &lt;code&gt;GET /api/me/export&lt;/code&gt; returns a full JSON dump; &lt;code&gt;POST /api/me/delete&lt;/code&gt; performs a soft-delete that redacts PII to &lt;code&gt;[REDACTED]&lt;/code&gt; and anonymizes audit actor IDs over a 30-day buffer.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Data Model
&lt;/h2&gt;

&lt;p&gt;Six MongoDB collections via Mongoose, with compound indexes tuned for real query patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;users&lt;/code&gt; — Clerk-linked accounts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;resumes&lt;/code&gt; — full resume documents (&lt;code&gt;{ userId: 1, createdAt: -1 }&lt;/code&gt;, plus org-visibility indexes)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;organizations&lt;/code&gt; — branding + settings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;organizationmembers&lt;/code&gt; — RBAC roles with a unique &lt;code&gt;{ organizationId, userId }&lt;/code&gt; compound index&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;auditlogs&lt;/code&gt; — immutable, TTL-expiring after 90 days&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;apikeys&lt;/code&gt; — HMAC service keys with scopes (&lt;code&gt;read:resumes&lt;/code&gt;, &lt;code&gt;write:resumes&lt;/code&gt;, &lt;code&gt;read:analytics&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Deployment Topology
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Vercel (Edge CDN)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend API&lt;/td&gt;
&lt;td&gt;Render (Docker)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;MongoDB Atlas (replica set)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache + Queue&lt;/td&gt;
&lt;td&gt;Redis Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LaTeX Renderer&lt;/td&gt;
&lt;td&gt;Docker container (&lt;code&gt;debian:bullseye-slim&lt;/code&gt; + TeX Live)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A two-stage GitHub Actions pipeline runs on every push/PR to &lt;code&gt;main&lt;/code&gt;: a Security &amp;amp; Code Quality job (&lt;code&gt;npm ci&lt;/code&gt;, &lt;code&gt;npm audit&lt;/code&gt;, lint, &lt;code&gt;build:deploy&lt;/code&gt;), followed by a Build Verification job that confirms the &lt;code&gt;dist/&lt;/code&gt; artifacts exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Offload heavy work immediately.&lt;/strong&gt; Moving LaTeX compilation to a BullMQ worker kept the API responsive and made retries and progress tracking trivial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design for AI failure.&lt;/strong&gt; LLMs return malformed JSON, time out, and rate-limit you. The circuit breaker plus a per-node "continue on failure" strategy made the AI features dependable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail open on infrastructure, fail closed on auth.&lt;/strong&gt; The rate limiter allows traffic if Redis dies; auth and RBAC reject by default. Choosing the right failure mode per subsystem matters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A shared Zod package pays off.&lt;/strong&gt; One source of truth for validation across frontend and backend eliminated an entire class of drift bugs.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What makes TexFolio different from other resume builders?&lt;/strong&gt;&lt;br&gt;
It compiles real LaTeX with &lt;code&gt;pdflatex&lt;/code&gt; instead of converting HTML to PDF, producing consistent, ATS-friendly output, and it scores resumes with a multi-agent LangGraph AI pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What tech stack does TexFolio use?&lt;/strong&gt;&lt;br&gt;
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 &lt;code&gt;pdflatex&lt;/code&gt; in Docker for rendering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does TexFolio handle AI provider outages?&lt;/strong&gt;&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is TexFolio GDPR-compliant?&lt;/strong&gt;&lt;br&gt;
It provides data export (&lt;code&gt;/api/me/export&lt;/code&gt;) and a right-to-erasure endpoint (&lt;code&gt;/api/me/delete&lt;/code&gt;) that anonymizes PII with a 30-day buffer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It / Star It
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://texfolio.vercel.app/" rel="noopener noreferrer"&gt;https://texfolio.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Source code (GitHub):&lt;/strong&gt; &lt;a href="https://github.com/theunstopabble/TexFolio" rel="noopener noreferrer"&gt;https://github.com/theunstopabble/TexFolio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub profile:&lt;/strong&gt; &lt;a href="https://github.com/theunstopabble" rel="noopener noreferrer"&gt;https://github.com/theunstopabble&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn:&lt;/strong&gt; &lt;a href="https://www.linkedin.com/in/gautamkr62/" rel="noopener noreferrer"&gt;https://www.linkedin.com/in/gautamkr62/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Author:&lt;/strong&gt; Gautam Kumar — &lt;a href="https://gautam-kr.vercel.app/" rel="noopener noreferrer"&gt;https://gautam-kr.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>ai</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
