DEV Community

Cover image for Next.js as a backend: honest limits for production apps
Nayan Kyada
Nayan Kyada

Posted on • Originally published at nayankyada.com

Next.js as a backend: honest limits for production apps

The Next.js backend question comes up on almost every project scoping call I take. A founder wants to ship fast, they already have Next.js on the frontend, and the natural question is: can we just use route handlers for the API too? The honest answer is yes, often — but with specific limits that will bite you if you ignore them.

What the Next.js backend actually gives you

Route handlers (app/api/*/route.ts) are proper HTTP endpoints. They run on Node.js or the Edge runtime, support all HTTP methods, can read cookies and headers, and stream responses. Server Actions give you a direct RPC-style bridge from client components to server code without writing an endpoint at all. For a large category of product work — form submissions, authenticated CRUD, sending emails via SendGrid, triggering Sanity mutations, webhook ingestion — this is genuinely sufficient.

I have production apps where the entire "backend" is a dozen route handlers and a handful of server actions. No Express server, no separate deployment, no ops overhead. It works.

// app/api/contact/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { sendEmail } from '@/lib/sendgrid';

export async function POST(req: NextRequest) {
  const body = await req.json();
  // basic validation omitted for brevity
  await sendEmail({ to: process.env.CONTACT_EMAIL!, ...body });
  return NextResponse.json({ ok: true });
}
Enter fullscreen mode Exit fullscreen mode

That's a real contact endpoint. Deployed to Vercel, zero extra infrastructure, cold starts are under 300ms in practice.

Where it genuinely holds up

Authenticated REST-style APIs for your own frontend. If the only consumer of your API is your Next.js frontend, route handlers are a perfect fit. You share types directly, you can use server actions instead of fetch calls, and you don't need to design a public API contract.

Lightweight webhook receivers. Stripe webhooks, Sanity webhooks, GitHub Actions callbacks — these are short-lived, low-concurrency endpoints. Route handlers handle them fine.

Server Actions for mutations. For forms, optimistic UI, and simple write paths, server actions remove a layer of indirection. The mental model is simpler and TypeScript inference works end-to-end.

Edge middleware for auth guards. Running token validation at the edge in middleware.ts before any page or API handler runs is genuinely useful and hard to replicate cheaply outside of Next.js.

The real limits — where it starts to break

Background jobs and queues. Vercel functions time out. The default is 10 seconds on Hobby, 60 seconds on Pro, 800 seconds on Enterprise. If you need to process a video, crunch a large dataset, or run a job that should survive a deploy, a route handler is the wrong tool. You need a proper queue — BullMQ on a VPS, Inngest, Trigger.dev — with Next.js acting only as the enqueue point.

Long-lived connections. WebSockets are not natively supported in Vercel's serverless model. You can use streaming responses for one-directional pushes, but anything requiring a persistent bidirectional channel needs a separate service — Ably, Pusher, Soketi, or a dedicated Node server.

Third-party API consumers. The moment a mobile app, a partner integration, or a separate service needs to call your API, you need versioning, stable contracts, proper auth (JWT issuance, OAuth), and documentation. Route handlers can technically serve this, but you're now fighting against Next.js's opinionated file structure rather than working with it. At that point, a standalone Fastify or Hono service is cleaner.

Heavy compute. CPU-bound tasks — image processing, PDF generation, ML inference — should not live inside a serverless function you're paying per-invocation for, especially when they might time out mid-work. Offload these to a dedicated worker.

Database connection pooling at scale. Every invocation of a serverless route handler can open a new database connection. With PostgreSQL this will exhaust your connection limit under real traffic unless you're using PgBouncer or Neon's HTTP driver. This isn't unique to Next.js, but it's a sharper problem here than on a traditional server that holds a single connection pool.

// This pattern will exhaust Postgres connections at scale on serverless
// Use @neondatabase/serverless or a connection pooler instead
import { Pool } from 'pg'; // ❌ new Pool() per cold start = connection leak

// Better:
import { neon } from '@neondatabase/serverless'; // ✅ HTTP-based, no persistent conn
const sql = neon(process.env.DATABASE_URL!);
Enter fullscreen mode Exit fullscreen mode

The pattern I actually use

For most marketing + product sites I build, Next.js handles 90% of the backend surface: content mutations via Sanity, auth via NextAuth, transactional emails, simple CRUD. That covers a founder's MVP and often the first 18 months of a real product.

When the project needs background processing or a public API, I introduce a second service — usually a lightweight Hono app deployed to a $6/month VPS or a Fly.io instance — and keep Next.js as the user-facing layer. The two services share a TypeScript types package if they're in the same monorepo. This boundary keeps Next.js doing what it's good at and removes the pressure to hack around serverless constraints.

The anti-pattern I see most often is teams discovering these limits after going to production and then spending two weeks retrofitting a queue system into a codebase that wasn't designed for it. Identifying the constraints early — specifically: do we need background jobs, do we need WebSockets, do we need a public API — takes 20 minutes in a scoping call and saves weeks later.

The verdict

Next.js is a capable backend for a well-defined category of work. It is not a general-purpose application server. Treat it like one and you'll hit a wall. Know the wall before you start building, and it'll serve you well for a long time.

Top comments (0)