DEV Community

Cover image for How I Built an AI Resume Builder with Next.js + OpenAI (Solo, in a Few Weeks)
Raj Sharma
Raj Sharma

Posted on

How I Built an AI Resume Builder with Next.js + OpenAI (Solo, in a Few Weeks)

I've been working as a full stack developer and kept seeing the same problem — people spend hours tweaking resume formatting instead of focusing on content. Most resume tools are either expensive, locked behind subscriptions, or require you to create an account just to see a template.

So I built CVForge — an AI-powered resume builder where you upload your CV, GPT-4 rewrites it for ATS and recruiters, you pick a template, and download as PDF or Word. No account required.

The Stack

  • Next.js 15 (App Router) — handles both frontend and API routes in one codebase
  • OpenAI API (GPT-4o) — resume extraction and AI optimization
  • Razorpay — payments (UPI, cards, wallets for Indian users)
  • MongoDB + Mongoose — payment persistence and admin panel
  • pdf-lib + docx — generating PDF and Word files server-side

How It Works

  1. User uploads a PDF or Word resume
  2. OpenAI extracts all structured data (name, experience, skills, education)
  3. User picks from 4 professional templates and previews instantly
  4. Optionally pays ₹15 for AI optimization — GPT-4 rewrites bullet points to be ATS-friendly and keyword-rich
  5. Pays ₹5 to download as PDF or Word

The Hard Parts

File generation server-side was trickier than expected. Generating pixel-perfect PDFs and DOCX files with proper formatting, tables, and 2-column layouts required a lot of trial and error with the docx package. Font sizes are in half-points, colors can't have # prefix — small things that cost hours.

iOS Safari downloads were a pain. Safari kills the user gesture activation after an async chain (like a Razorpay payment popup), so window.open() gets blocked. Fix: open window.open('about:blank', '_blank') synchronously before any await, then navigate it after the fetch completes.

// Open window synchronously before any await
const iosWin = window.open('about:blank', '_blank')

// ... do your async work ...

// Then navigate it after fetch completes
iosWin.location.href = url
Enter fullscreen mode Exit fullscreen mode

Razorpay on mobile — on iOS specifically, skip auto-download after payment and show a toast asking the user to tap the download button again with a fresh gesture.

What I'd Do Differently

  • Start with SSR in mind from day one. I had all my landing page content in a 'use client' component wrapped in Suspense with useSearchParams — which meant crawlers saw an empty page. Fixed it by passing searchParams as props from the server component instead.
  • Build the admin panel earlier. Flying blind without usage stats is frustrating.

Result

Live at cvforge.in — just launched on Product Hunt today.

If you're building something similar or have feedback, drop a comment. Always happy to talk Next.js + AI integrations.

Top comments (2)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.