DEV Community

xiaowei
xiaowei

Posted on • Originally published at jobmirror.app

How I Built a Free AI Job Offer Comparison Tool on Cloudflare Pages (Next.js + Edge Runtime)

I built JobMirror — a free AI toolkit for job seekers — and deployed it entirely on Cloudflare Pages with Next.js. Here's what I learned, what broke, and how I fixed it.

Why I Built It

Most job seekers face the same three problems:

  1. They apply to jobs without knowing how well their resume actually matches
  2. They accept the first offer they get without comparing alternatives
  3. They leave salary on the table because they don't have data

I wanted to fix all three with a single, free tool. The result is JobMirror: resume review, job fit analysis, offer comparison, career assessment, and cover letter generation in one place.

The Stack

  • Next.js 14 (App Router)
  • Cloudflare Pages with @cloudflare/next-on-pages
  • DeepSeek API for AI inference
  • next-auth v5 beta for Google OAuth
  • No database — JWT sessions + localStorage

The Hard Parts

1. Edge Runtime Is Not Node.js

Cloudflare Pages runs your code on V8 isolates, not Node.js. This means:

  • No fs, no crypto from Node
  • No prisma (uses native bindings)
  • Any library that assumes Node.js internals will silently fail or loudly crash

Every API route needs this at the top:

export const runtime = 'edge';
Enter fullscreen mode Exit fullscreen mode

Forget it on one route and you get a cryptic build error or a 500 in production.

2. next-auth v4 Breaks on Edge

next-auth v4 imports Node's crypto module internally. On Cloudflare Pages, this throws:

Module not found: Can't resolve 'crypto'
Enter fullscreen mode Exit fullscreen mode

The fix: upgrade to next-auth v5 beta (5.0.0-beta.30), which is built for Edge from the ground up.

npm install next-auth@5.0.0-beta.30
Enter fullscreen mode Exit fullscreen mode

v5 also changes the config shape significantly — AUTH_SECRET instead of NEXTAUTH_SECRET, and the provider setup is slightly different. Worth it though.

3. The Correct Build + Deploy Command

This tripped me up for a while. The output directory is NOT .next — it's .vercel/output/static:

# Build
npx @cloudflare/next-on-pages@1

# Deploy
CLOUDFLARE_API_TOKEN="your_token" \
CLOUDFLARE_ACCOUNT_ID="your_account_id" \
npx wrangler pages deploy .vercel/output/static \
  --project-name=your-project \
  --branch=main
Enter fullscreen mode Exit fullscreen mode

Deploying .next directly results in a site that returns 404 or 500 on every route. Ask me how I know.

4. SessionProvider Can't Be in a Server Component

If you put <SessionProvider> directly in layout.tsx, Next.js tries to prerender it as a server component and throws during build. Wrap it:

// components/ClientProviders.tsx
'use client';
import { SessionProvider } from 'next-auth/react';

export default function ClientProviders({ children }: { children: React.ReactNode }) {
  return <SessionProvider>{children}</SessionProvider>;
}
Enter fullscreen mode Exit fullscreen mode

Then use <ClientProviders> in your layout — works cleanly.

5. AI on the Edge

For AI inference, I'm calling DeepSeek's API directly from Edge functions. Since it's just HTTP, it works perfectly — no special SDK needed:

export const runtime = 'edge';

export async function POST(req: Request) {
  const { resume, jobDescription } = await req.json();

  const response = await fetch('https://api.deepseek.com/v1/chat/completions', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${process.env.DEEPSEEK_API_KEY}`,
    },
    body: JSON.stringify({
      model: 'deepseek-chat',
      messages: [
        { role: 'system', content: 'You are a career coach...'},
        { role: 'user', content: `Resume: ${resume}\n\nJob: ${jobDescription}` }
      ],
      stream: true,
    }),
  });

  return new Response(response.body, {
    headers: { 'Content-Type': 'text/event-stream' },
  });
}
Enter fullscreen mode Exit fullscreen mode

Streaming works out of the box because Edge functions support ReadableStream natively.

What I'd Do Differently

  • Start with Edge constraints in mind. Don't pick libraries and then discover they don't work on Edge — check first.
  • Use Cloudflare D1 or KV early. I'm using localStorage for user data right now, which works but isn't ideal for any kind of persistence.
  • Don't use pnpm with Cloudflare Pages. The lockfile version mismatch errors are frequent and annoying. Use npm.

The Result

JobMirror is live and free to use:

No account required for core tools. Feedback welcome — especially if you've hit similar Edge Runtime issues.


Built with Next.js 14, Cloudflare Pages, and more late nights than I'd like to admit.

Top comments (0)