← All projects

ResumeBanao

An AI resume builder with an LLM ATS-scoring engine that gives section-level feedback, pixel-perfect PDF export, OAuth and payments. One codebase serving two domains, with a full SEO engine underneath.

Role
Solo builder & operator
Status
Live in production
Backend
Next.js 16 · Prisma · Postgres

Why I built it

Most resumes die inside applicant tracking systems before a human ever reads them, and most resume tools only care about how the page looks. I wanted a builder that treats the ATS as a first-class audience: score the resume the way a parser sees it, tell the user exactly which section is weak and why, then let an LLM rewrite it with them.

It also had to work as a product for the Indian market ("banao" is Hindi for "make it"), which meant Razorpay payments, Google sign-in, and PDFs that come out identical to the on-screen preview every single time.

What I built

Resume builder with live preview

Structured editor over a JSON resume model with template rendering, so content and design stay cleanly separated.

LLM ATS-scoring engine

Scores the resume and returns section-level feedback: which parts an ATS parses badly, which bullets are weak, and what to fix first.

AI improve

One-click LLM rewriting of bullets and summaries, wired through an Anthropic-format endpoint with the raw resume as grounding context.

Serverless PDF rendering

Puppeteer with @sparticuz/chromium running inside Vercel functions, producing pixel-perfect PDFs of the exact preview markup.

Auth & payments

Auth.js v5 with Google OAuth plus email flows, and Razorpay for paid features. Verification and forgot-password emails via SMTP.

A real SEO engine

Server-component metadata, JSON-LD (Organization, WebSite, SoftwareApplication, FAQ), a 21-URL sitemap, 8 long-tail guides, and Search Console + Bing verification.

Architecture

Everything runs inside one Next.js 16 app on Vercel: server components for SEO-critical pages, API routes for the AI and PDF services, Prisma over Postgres for persistence. Two domains point at the same deployment, with one canonical auth host so OAuth cookies never split across domains.

Users 2 domains · 1 app VERCEL · NEXT.JS 16 App Router (RSC) builder · guides · metadata API Routes ats-check · improve · auth PDF Service Puppeteer · @sparticuz/chromium PostgreSQL Prisma ORM DeepSeek LLM OpenAI fmt · ats-check Anthropic fmt · improve Google OAuth Auth.js v5 Razorpay paid features Search GSC · Bing · GA4

Stack: Next.js 16 (Turbopack), React, TypeScript, Prisma, PostgreSQL, Auth.js v5 (Google OAuth), Razorpay, Puppeteer + @sparticuz/chromium, DeepSeek LLM API, nodemailer, Vercel, GA4 + Search Console + Bing.

Hard problems I actually hit

OAuth broke the moment the app had two domains

After the rebrand the app served on both the old domain and resumebanao.com, but the auth host was still pinned to the old one. Logging in from the new domain built sign-in and callback URLs on the other domain, CSRF and state cookies got lost across origins, and every login ended in error=Configuration. The fix was to make one domain the canonical auth host: repoint NEXTAUTH_URL, add the matching Google redirect URI, and let the other domain funnel into it. Cross-domain auth is a cookie problem before it is a code problem.

The AI features silently returned nothing

The ATS engine runs on DeepSeek's reasoning model, and reasoning models put a thinking block first in Anthropic-format responses. My parser read content[0].text, which was the hidden reasoning slot, so users got empty feedback with no error anywhere. The fix: find the first block with type:"text" instead of trusting position. Provider response shapes are part of your contract; test them per model, not per API.

Pixel-perfect PDFs inside a serverless function

Full Chrome does not fit in a Vercel function, so PDF export runs @sparticuz/chromium, a trimmed headless build, against the same markup as the live preview. Fonts, page sizing and cold-start behavior all needed tuning so the downloaded PDF matches the on-screen resume exactly, every time, within serverless memory and time limits.

Rebranding without orphaning anyone's data

The product started as ResumeKraft, and unsaved work lived in the browser under resumekraft_* localStorage keys. A clean rename would have silently wiped users' drafts. So the brand changed across 112 strings, layouts, legal pages and emails, while the storage keys deliberately stayed on the old names. Users saw a new product and kept every byte of their data.

The dynamic OG image that was 0 bytes in production

The next/og ImageResponse route worked locally and returned an empty file on Vercel, which meant every social share card was blank. Rather than fight the edge runtime, I generated the OG image once, shipped it as a static PNG, and referenced it from metadata. Boring beats clever when the clever path fails silently.

By the numbers

21
URLs in the sitemap
8
Long-tail SEO guides
2
Domains, one codebase
4
JSON-LD schema types
← All projects Next: JobPortal Auto-Apply →