What Is Serverless? Functions and Edge Computing Explained for AI Coders
When you deploy a Next.js app to Vercel, your API routes run as serverless functions. When AI generates backend logic for you, it may assume serverless. Here's what that means, the tradeoffs, and when to use it vs. a traditional server.
TL;DR
Serverless means your code runs as individual functions triggered by requests — no server you manage, no server running when idle. Vercel's API routes, AWS Lambda, and Netlify Functions are all serverless. You pay per-request, not per-hour. The tradeoffs: cold starts (first request can be slow), no persistent state between requests, and database connections need special handling. Best for: variable traffic, side projects, APIs that don't need persistent connections.
Why AI Coders Need to Understand Serverless
If you deploy a Next.js app to Vercel (the default recommendation for Next.js), your /app/api/ routes automatically become serverless functions — even if you never explicitly chose serverless. These are essentially API endpoints that run on demand. Understanding this explains:
- Why storing data in a global variable between requests doesn't work (each invocation is fresh)
- Why database connections behave differently vs. a traditional Express server
- Why your first request to a cold endpoint is slower than subsequent ones
- Why you occasionally see "function timeout" errors on long operations
Real Scenario: A Next.js API Route
"Build a Next.js API route that accepts a POST request with a user's email and saves it to my PostgreSQL database."
// app/api/subscribe/route.js
// This file becomes a serverless function on Vercel automatically
// Each request runs this in an isolated environment
import { prisma } from '@/lib/prisma'
export async function POST(request) {
const { email } = await request.json()
if (!email || !email.includes('@')) {
return Response.json({ error: 'Invalid email' }, { status: 400 })
}
try {
await prisma.subscriber.create({ data: { email } })
return Response.json({ success: true })
} catch (error) {
if (error.code === 'P2002') { // Prisma unique constraint violation
return Response.json({ error: 'Already subscribed' }, { status: 409 })
}
return Response.json({ error: 'Server error' }, { status: 500 })
}
}
// When this runs on Vercel:
// - Vercel spins up a fresh Node.js environment
// - Runs the function
// - Returns the response
// - May shut down the environment (or keep it warm for more requests)
// Each invocation is potentially isolated from every other
How Serverless Works
Traditional Server vs. Serverless
| Traditional Server (VPS) | Serverless Functions | |
|---|---|---|
| Running when idle | Yes — costs money 24/7 | No — shuts down between requests |
| Scaling | Manual (upgrade server) | Automatic (0 to millions of requests) |
| State between requests | Persistent (variables survive) | Ephemeral (fresh each time) |
| Cost model | Per hour (paying always) | Per request (paying only when used) |
| Cold starts | None | 100ms–2000ms on first request |
| Max duration | Unlimited | 10–900 seconds (varies by platform) |
| WebSockets | Supported | Not supported (request-response only) |
Cold Starts: The Main Tradeoff
When a serverless function hasn't been called recently, the cloud provider has to boot up a fresh environment before it can run your code. This startup takes anywhere from 100ms to 2 seconds. After that first request, subsequent requests are fast because the environment stays "warm."
What this means for you: the first visitor to hit your API after a quiet period gets a slower response. Everyone after that is fine. Here's how to deal with it:
- Keep functions small: Fewer dependencies = faster cold start. Don't import entire libraries when you need one function.
- Edge functions: Vercel Edge Functions and Cloudflare Workers run in V8 isolates (not Node.js), with cold starts under 1ms. Tradeoff: limited runtime APIs.
- Keep functions warm: Ping them periodically via a cron job to prevent cold starts on critical endpoints.
Database Connections in Serverless
Traditional servers open a database connection once on startup and reuse it. Serverless functions may spin up hundreds of instances simultaneously, each opening its own connection. A PostgreSQL server handles ~100 connections before degrading.
// The problem: each serverless instance opens its own connection
// 100 concurrent requests = 100 database connections = overloaded DB
// The solution: use a connection pooler (PgBouncer or Supabase Pooler)
// or Prisma Accelerate
// In your Prisma schema:
datasource db {
provider = "postgresql"
// For serverless: use pooled connection URL
url = env("DATABASE_URL") // Pooled (port 6543 for Supabase)
directUrl = env("DIRECT_URL") // Direct (port 5432) for migrations only
}
// Or use a singleton Prisma client (reuses connection within a warm function):
// lib/prisma.js
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma
What AI Gets Wrong About Serverless
1. Storing State in Global Variables
// BROKEN in serverless — in-memory state doesn't persist between requests
let requestCount = 0 // This resets every time a cold start happens
export async function GET() {
requestCount++ // This counter is meaningless in serverless
return Response.json({ count: requestCount })
}
// FIX: Store state in a database, Redis, or a KV store
2. Opening Database Connections Without a Singleton
// BAD: Creates a new Prisma client (new connection) on every request
export async function POST(request) {
const prisma = new PrismaClient() // New connection every time!
// ...
}
// GOOD: Import singleton from a shared module
import { prisma } from '@/lib/prisma' // Reused in warm function instances
3. Long-Running Operations That Exceed Timeouts
Vercel Hobby: 10-second max. Vercel Pro: 60-second max. Long AI inference calls, large file processing, or slow external APIs can exceed these limits. Move long operations to a queue-based background job system.
Serverless vs. Edge Functions
Vercel offers two serverless options:
- Serverless Functions: Full Node.js runtime. Can use any npm package. Cold start: 100ms–1s. Run in one region.
- Edge Functions: A lighter runtime that starts almost instantly and runs closer to your users worldwide. Tradeoff: you can't use all Node.js features or npm packages.
Default to Serverless Functions. Use Edge only for very latency-sensitive operations (authentication checks, personalization) where you can live within the constraints.
What to Learn Next
Frequently Asked Questions
What does serverless mean?
Serverless means your code runs as on-demand functions without you managing server infrastructure. The cloud provider handles scaling, uptime, and servers. You write functions, deploy them, and pay only for actual execution time — not for a server running 24/7. "Serverless" doesn't mean no servers; it means you don't manage them.
What is a cold start in serverless?
A cold start happens when your function hasn't run recently and the cloud provider must spin up a new instance: provisioning a container, loading your code and dependencies, initializing connections. This adds 100ms–2000ms of latency to the first request. Functions used frequently stay "warm." Critical user-facing endpoints may need warming strategies or edge functions.
When should I use serverless vs a traditional server?
Serverless: unpredictable or spiky traffic, zero infrastructure management desired, functions under 30 seconds, side projects that may get zero traffic most days. Traditional server (VPS): persistent connections (WebSockets, SSE), long-running processes, consistent high-volume traffic (where per-request costs exceed VPS costs), or operations that require Node.js APIs unavailable in serverless.
Are Vercel API routes serverless?
Yes. Every file in /app/api/ in a Next.js app deployed to Vercel becomes a serverless function automatically. Each route runs in isolation, scales to handle traffic spikes, and you pay per invocation. The free tier includes enough for most side projects. This happens transparently — no extra configuration required.
What does AI get wrong about serverless?
AI commonly generates code that stores state in global variables (which doesn't persist between invocations), creates new database connections per request (exhausting connection limits at scale), and doesn't account for execution time limits. Always check for global state and ensure you're using a Prisma singleton pattern or connection pooler when deploying to serverless platforms.