FNA Technology Header Logo
ServicesWorkAboutBlog
[ start a project ]
FNA Technology Footer Logo

Transforming your digital vision into reality. Software, AI, web & mobile — built for outcomes.

[email protected] footer link+91 8879510299 footer link
Share page:
MAIN
HomeOur ServicesProjectsCompany Blog
COMPANY
About UsContact UsLinkedIn
LEGAL
Privacy PolicyTerms & Conditions
© 2026 FNA TECHNOLOGY LLP — ALL RIGHTS RESERVEDIndia · UK · Middle East
Edge Functions vs. API Routes: How to ChooseOptimize web performance and database connectivity at the edge. Learn how serverless edge functions reduce latency and improve resource efficiency.Business owners, developers, CTOsedge functions vs API routes, serverless edge functions, Cloudflare Workers vs Lambda, Next.js edge runtime, serverless architecture, edge computing web development, Vercel edge functionsFNA Technology
Web Development

Edge Functions vs. API Routes: How to Choose

May 4, 2026
8 min read
FNA Technology
Serverless architecture diagram showing edge functions and API routes working together

The short version: Edge functions and serverless API routes solve different problems. Edge is for low-latency request manipulation — auth checks, redirects, A/B testing — and runs in 50ms or less, close to the user. API routes handle database writes, heavy computation, and business logic. Using them wrong costs you either latency or reliability.

Most teams pick one and overuse it. Either they put everything at the edge and hit database connection walls, or they route everything through a regional API and wonder why their auth checks feel slow. The right answer is a split architecture — and understanding which work belongs where.

What edge functions actually are

Edge functions run on a global CDN network — Cloudflare's ~300 PoPs, Vercel's Edge Network, Netlify's edge infrastructure. When a user in Singapore makes a request, an edge function handles it from a nearby PoP in under 5ms, rather than routing to a server in us-east-1 that adds 200ms of network latency.

The tradeoff: edge functions run in constrained V8 isolates. No persistent TCP connections. Strict CPU time limits (30–50ms depending on platform). No access to the Node.js standard library's filesystem or child_process APIs. A small subset of Web APIs only.

Code
// Cloudflare Worker — runs at the edge, executes in <5ms
export default {
  async fetch(request: Request): Promise<Response> {
    const country = request.cf?.country;

    // Redirect UK users to the UK-specific subdomain
    if (country === 'GB') {
      return Response.redirect('https://uk.example.com' + new URL(request.url).pathname, 301);
    }

    // Check auth token without a database call
    const token = request.headers.get('Authorization')?.split(' ')[1];
    if (!token || !isValidJWT(token)) {
      return new Response('Unauthorized', { status: 401 });
    }

    // Pass to origin for actual processing
    return fetch(request);
  }
};

No database. No file system. Fast.

What serverless API routes actually are

API routes — AWS Lambda, Vercel Functions (non-edge), Netlify Functions — run in full Node.js (or Python, Go, Ruby) containers in a fixed region. They have access to the full runtime, can hold database connections (briefly), and can run for up to 15 minutes on Lambda.

The tradeoff: they're regional. A Lambda in us-east-1 adds 200–250ms round-trip latency for users in Europe or Asia-Pacific. And they have the cold start problem — if a function hasn't been invoked recently, the first request pays a 100–500ms penalty while the container initializes.

Code
// Next.js API route — runs in a regional Node.js container
// app/api/orders/route.ts
import { db } from '@/lib/database';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
  const body = await request.json();

  // Database write — this is what edge functions cannot do
  const order = await db.order.create({
    data: {
      userId: body.userId,
      items: body.items,
      total: body.total,
    },
  });

  // Trigger background job
  await queue.dispatch('send-order-confirmation', { orderId: order.id });

  return NextResponse.json({ orderId: order.id }, { status: 201 });
}

Full database access. Full Node.js runtime. Regional latency.

The decision matrix

Work typeEdge functionAPI route
Auth token validation (JWT, session cookie)Yes — no DB needed for stateless tokensOnly if token validation requires a DB lookup
Geolocation-based redirectsYesNo — too slow
A/B testing / feature flagsYes — serve different responses by cohortOnly if flag config requires a DB call
Rate limiting (approximate)Yes — using Cloudflare's KV or Durable ObjectsFor exact rate limiting requiring persistent state
Database readsNo — use edge-compatible DB (Neon, PlanetScale) or pass to APIYes
Database writesNoYes
Payment processingNo — too sensitive for edge, use regional for auditabilityYes
Image / PDF generationNoYes
WebSocket connectionsCloudflare Durable Objects onlyYes (with persistent servers)
Sending emailNoYes
Heavy computation (>50ms CPU)NoYes

The split architecture pattern

The pattern that works in production: edge handles auth and routing, API handles data.

Code
User Request
    ↓
Edge Function (Cloudflare Worker / Vercel Edge)
  - Validate JWT (stateless, no DB)
  - Check geolocation, apply redirects
  - Rewrite headers, add request ID
  - Block known malicious patterns
    ↓ (passes enriched request)
Regional API Route (Lambda / Vercel Function)
  - Connect to database
  - Execute business logic
  - Write records
  - Dispatch background jobs
    ↓
Response cached at edge for subsequent requests

In Next.js, this is implemented via the middleware.ts file for edge logic and app/api/ routes for API logic:

Code
// middleware.ts — runs at edge on every request
import { NextRequest, NextResponse } from 'next/server';
import { verifyJWT } from '@/lib/auth';

export const config = {
  matcher: ['/api/:path*', '/dashboard/:path*'],
};

export async function middleware(request: NextRequest) {
  const token = request.cookies.get('session')?.value;

  if (!token) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // JWT verification is CPU-fast — no DB call needed
  const payload = await verifyJWT(token);
  if (!payload) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  // Attach user ID to request headers for the API route
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-user-id', payload.sub);

  return NextResponse.next({ request: { headers: requestHeaders } });
}

The edge middleware validates the session token without hitting the database — JWT verification is pure computation. The user ID is forwarded in a request header so the API route doesn't have to repeat the auth check.

Database connections in serverless: the connection pool problem

This is the most common failure mode in serverless architectures. Each Lambda invocation opens a new database connection. Under concurrent load, 500 simultaneous Lambda invocations means 500 simultaneous connections. PostgreSQL's default max_connections is 100. The database falls over.

The fix is a connection pooler:

Code
# PgBouncer config — runs as a sidecar or separate instance
# /etc/pgbouncer/pgbouncer.ini

[databases]
mydb = host=rds.endpoint.amazonaws.com port=5432 dbname=mydb

[pgbouncer]
listen_port = 6432
listen_addr = 0.0.0.0
auth_type = md5
pool_mode = transaction        # transaction pooling — best for serverless
max_client_conn = 1000         # Lambda connections to PgBouncer
default_pool_size = 20         # PgBouncer connections to RDS

With transaction-mode pooling, PgBouncer holds 20 connections to the database but serves up to 1,000 Lambda clients. Each Lambda invocation borrows a connection for the duration of a transaction, then returns it immediately.

AWS RDS Proxy provides the same capability as a managed service — worth it if you're already on RDS and don't want to operate PgBouncer yourself.

Edge-compatible databases

If you need data access at the edge, you need a database that speaks HTTP, not TCP:

DatabaseProtocolEdge compatibleNotes
Neon (serverless Postgres)HTTP + WebSocketYesPostgres-compatible, HTTP API for edge
PlanetScaleHTTPYesMySQL-compatible, branching model
Cloudflare D1SQLite via Workers APIWorkers onlyTight Cloudflare integration
Cloudflare KVHTTPYesKey-value only, eventually consistent
Upstash RedisHTTPYesRedis-compatible, per-request billing
Traditional Postgres (RDS)TCPNoUse in regional API routes only

For most applications, the right answer is: use traditional Postgres in your API routes (with a connection pooler), and use Upstash Redis or Cloudflare KV at the edge for session lookups, feature flags, and rate limiting state.

Real-world example: auth + data in a Next.js app

Code
// 1. Edge middleware validates session token (no DB)
// middleware.ts
export async function middleware(request: NextRequest) {
  const sessionToken = request.cookies.get('session')?.value;
  const payload = sessionToken ? await verifyJWT(sessionToken) : null;

  if (!payload && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  return NextResponse.next();
}

// 2. API route reads user data from Postgres (regional, with connection pool)
// app/api/user/profile/route.ts
import { db } from '@/lib/db'; // Prisma + PgBouncer

export async function GET(request: NextRequest) {
  const userId = request.headers.get('x-user-id'); // set by edge middleware

  const user = await db.user.findUnique({
    where: { id: userId },
    select: { name: true, email: true, plan: true },
  });

  return NextResponse.json(user);
}

// 3. Static pages cached at edge — no function invocation needed
// app/blog/[slug]/page.tsx
export const revalidate = 3600; // Cache for 1 hour at CDN edge

The auth check runs at the edge in <5ms. The database query runs regionally in ~20ms. Static pages are served directly from CDN cache with zero function invocations.

For teams building Laravel backends that connect to similar serverless frontend architectures, the Laravel developer guide covers the API design patterns that pair well with edge-authenticated frontends.

Frequently Asked Questions

Not reliably. Traditional PostgreSQL requires a persistent TCP connection with a handshake that takes 50–200ms. Edge functions have execution time limits of 30–50ms on most platforms and are distributed across hundreds of PoPs, making persistent database connections impossible. The exception is databases designed for edge: PlanetScale (HTTP-based MySQL), Neon (serverless Postgres with HTTP API), and Cloudflare D1 all work at the edge because they use stateless HTTP protocols instead of persistent connections.

Cold start occurs when a serverless function hasn't been invoked recently and the platform needs to spin up a new container to handle the request. For AWS Lambda with Node.js, cold starts range from 100–500ms. For Lambda with Java or .NET, they can reach 2–5 seconds. Edge functions avoid cold starts entirely because they run on V8 isolates that initialize in under 5ms. For user-facing endpoints where latency matters, edge eliminates this problem — but only for the logic that can run without a database connection.

Cloudflare Workers: 50ms CPU time per request (wall clock time is longer due to async I/O). Vercel Edge Functions: 30 seconds wall clock time. Netlify Edge Functions: 50ms CPU time. These limits are designed for request manipulation tasks, not heavy computation. If your function is doing significant processing — PDF generation, image resizing, complex database queries — it belongs in a standard serverless function or dedicated server, not at the edge.

Cloudflare Workers: when you need sub-10ms latency globally, request interception (auth, redirects, header manipulation), or logic that must run before the origin server is hit. AWS Lambda: when you need more than 50ms CPU time, persistent database connections, access to other AWS services (S3, SQS, RDS), or execution times up to 15 minutes. They are not alternatives — most architectures use both.

Serverless functions can exhaust database connection pools because each function invocation opens a new connection and doesn't close it reliably. The standard solution is a connection pooler: PgBouncer for PostgreSQL (runs as a sidecar on EC2 or a separate instance), RDS Proxy for AWS RDS/Aurora, or Prisma Accelerate for Prisma users. For high-concurrency serverless APIs, connection pooling is not optional — it's what keeps your database from hitting max_connections under load.

#edge functions vs API routes#serverless edge functions#Cloudflare Workers vs Lambda#Next.js edge runtime#serverless architecture#edge computing web development#Vercel edge functions
Share this article:
FNA Technology

Written by

FNA Technology

Team Member at FNA Technology

FNA Technology is a software development company specializing in AI, mobile apps, and web solutions.

Work with us