Moving from Next.js API Routes to Cloud Functions: A Migration Checklist
When static export killed our /api/* routes, we migrated everything to Cloud Functions behind Firebase Hosting rewrites. This is the checklist we wish we'd had on day one.
Step 1: Inventory every endpoint
Grep the codebase, not your memory:
grep -roh "api/[a-z0-9/_-]*" apps/* --include="*.ts" --include="*.tsx" | sort -u
We found fourteen endpoints we'd half-forgotten: contact forms, comment counts, newsletter signups, trial requests, and a full admin CRUD surface.
Step 2: Preserve URLs with rewrites
Don't change the frontend to call new URLs. Rewrite instead:
{ "source": "/api/**", "function": "apiGateway" }
The apps keep calling /api/contact exactly as before. Migration becomes invisible to the client.
Step 3: One function, one routing table
We consolidated into a single apiGateway function. Fewer cold starts to tune, one rate limiter, one place for validation. If your endpoints need different scaling profiles, split later — consolidate first.
Step 4: Move secrets deliberately
Environment variables move from .env.local to Firebase function params:
const SMTP_PASS = defineString('SMTP_PASS')
Rotate them during migration, don't copy. Treat the migration as the rotation event.
Step 5: Test with the emulator before deploy
firebase emulators:start runs functions locally with the same rewrites. Test every endpoint with curl, including failure cases — invalid email, missing token, rate limit exceeded.
Step 6: Deploy functions first, frontend second
Deploy the function, verify rewrites serve /api/health, then ship frontend changes that depend on it. The old frontend keeps working against the new backend the whole time.
Step 7: Watch cold starts
First call after idle takes longer. If it matters, configure minimum instances. For a family project it doesn't — for a customer-facing checkout it does.
The whole migration took a day. The checklist is now our template for any backend move.