· 8 min read · updated
Edge Functions in Practice
Edge functions promise faster responses by running code near your users. Here's what works at the edge, what doesn't, and when the latency matters.
By Tanner Goins

The pitch for edge functions is simple: run your server-side code on servers distributed around the world, so every user gets a response from the nearest location. Instead of all requests hitting a single region, they hit whichever point of presence is closest.
Faster responses. Lower latency. Happy users.
The reality is more nuanced. I’ve deployed several projects to edge runtimes, including parts of this site on Vercel’s Edge Network, and I’ve found that edge functions are powerful for specific use cases and frustrating for others. The difference between “this should run at the edge” and “this should stay in a region” isn’t always obvious until you’ve tried both. (This site has since moved to Cloudflare Workers, where nearly every page is a static file.)
What “The Edge” Actually Means
When we say “edge,” we mean a network of servers distributed geographically, often called points of presence (PoPs). Cloudflare has PoPs in over 300 cities. Vercel’s Edge Network spans dozens of regions. AWS CloudFront has locations worldwide.
When your code runs at the edge, it executes on whichever server is physically closest to the user making the request. A user in Tokyo hits a Tokyo edge server. A user in London hits a London server. The round trip between the user and your code is measured in single-digit milliseconds instead of the hundreds of milliseconds it might take to reach a server in Virginia.
The key constraint: edge functions run in a limited runtime. They’re not full Node.js environments. Most edge runtimes use V8 isolates (the same engine that powers Chrome) with a restricted API surface. No filesystem access. No native modules. Limited execution time. Restricted memory.
This is the fundamental tradeoff: lower latency, but a smaller toolbox.
What Runs Well at the Edge
Authentication and Authorization
Checking whether a user is authenticated (validating a JWT, reading a session cookie, checking permissions) is a perfect edge use case. It’s fast, stateless, and benefits enormously from low latency because it happens on every request.
// Vercel Edge Middleware example
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("session");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/:path*", "/api/protected/:path*"],
}; This runs before the request even reaches your application server. Authentication failures are caught at the edge, saving a round trip to your origin.
Redirects and Rewrites
URL redirects, A/B test routing, geographic-based routing, and request rewriting are fast, stateless operations that benefit from edge execution.
// Geo-based routing
export function middleware(request: NextRequest) {
const country = request.geo?.country || "US";
if (country === "DE") {
return NextResponse.rewrite(
new URL("/de" + request.nextUrl.pathname, request.url),
);
}
return NextResponse.next();
} This kind of routing adds near-zero latency at the edge. Doing it in a regional server adds a network round trip that users notice.
Personalization at the Edge
Adding user-specific headers, customizing responses based on location, or injecting A/B test variants can happen at the edge without hitting your origin:
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Assign A/B test variant based on cookie
if (!request.cookies.has("ab-variant")) {
const variant = Math.random() < 0.5 ? "A" : "B";
response.cookies.set("ab-variant", variant);
}
return response;
} This pairs well with feature flag strategies: the edge decides which variant to serve before the page even starts rendering.
API Response Caching
Edge functions can cache API responses geographically. A user in Sydney requesting product data gets a cached response from a Sydney edge node instead of waiting for your Virginia server to query the database.
export const config = { runtime: "edge" };
export async function GET(request: Request) {
const cacheKey = new URL(request.url).pathname;
const cached = await caches.default.match(cacheKey);
if (cached) return cached;
const data = await fetch("https://api.example.com/products");
const response = new Response(JSON.stringify(await data.json()), {
headers: {
"Content-Type": "application/json",
"Cache-Control": "s-maxage=300", // Cache for 5 minutes
},
});
// Store in edge cache
await caches.default.put(cacheKey, response.clone());
return response;
} Static Asset Optimization
Image transformation, content compression, and header injection for static assets all benefit from edge execution. This is what CDNs have been doing for years. Edge functions just make it programmable.
What Doesn’t Work at the Edge
Database Queries
This is the biggest surprise for developers new to edge functions. Your code is at the edge, but your database isn’t. A PostgreSQL database lives in one region. If your edge function in Tokyo needs to query a database in Virginia, you still pay the cross-continent latency, and now you’ve added the complexity of an edge deployment without the latency benefit.
The exception: Globally distributed databases like CockroachDB, PlanetScale (MySQL), Turso (SQLite at the edge), or Cloudflare D1 have replicas in multiple regions. If your data is at the edge too, the latency savings are real. But most applications use a single-region database, and for those, edge functions that query the database are slower, not faster, than a regional function co-located with the database.
My rule: If the function needs the database, run it in the same region as the database. If it doesn’t, consider the edge.
Heavy Computation
Edge runtimes have strict CPU and memory limits. A typical edge function gets 50ms–128ms of CPU time and 128MB of memory. Complex data processing, image generation, PDF rendering, or ML inference won’t fit.
These workloads belong on regional servers or dedicated compute, not the edge.
Long-Running Processes
Edge functions are designed for request-response cycles under 30 seconds (often much less). Background jobs, webhooks that require processing time, and streaming connections aren’t a good fit.
Node.js-Specific APIs
Edge runtimes don’t support the full Node.js API. No fs module. No child_process. No native addons. Libraries that depend on these won’t work. This eliminates many popular packages, so always check compatibility before deploying to the edge.
The Latency Math
Here’s when edge functions actually save meaningful time.
When the Edge Wins
Scenario: Auth check + static content serving.
- Regional: User in Sydney → Virginia server (180ms round trip) → response
- Edge: User in Sydney → Sydney edge (5ms) → response
- Savings: ~175ms. Noticeable improvement.
Scenario: Redirect/rewrite logic.
- Regional: User → Origin → redirect response → User → Origin → final response (2 round trips)
- Edge: User → Edge → redirect → Edge → final response (resolved locally)
- Savings: One full round trip. Significant.
When the Edge Doesn’t Help
Scenario: Edge function queries regional database.
- Regional: User → Virginia server (180ms) → database (2ms) → response back (180ms) = ~362ms
- Edge: User → Sydney edge (5ms) → Virginia database (180ms round trip) → response back (5ms) = ~370ms
- No savings. Actually slightly slower due to the extra hop.
Scenario: Edge function calls a regional API.
- Same math as the database case. If the upstream service is in one region, the edge function still needs to reach it.
The Decision Framework
Ask: Where does the data live?
- Data is static or cached → Edge wins
- Data is at the edge (distributed DB, KV store) → Edge wins
- Data is in one region → Regional wins
- No data needed (pure computation on the request) → Edge wins
Edge in SvelteKit
SvelteKit supports edge deployment through adapter configuration. On Vercel, certain routes can run at the edge while others run in a regional serverless function:
// svelte.config.js with Vercel adapter
import adapter from "@sveltejs/adapter-vercel";
export default {
kit: {
adapter: adapter({
runtime: "edge", // Default: all routes at the edge
regions: ["iad1"], // Or pin to a specific region
}),
},
}; For per-route configuration:
// src/routes/api/fast-endpoint/+server.ts
export const config = {
runtime: "edge",
};
// src/routes/api/db-endpoint/+server.ts
export const config = {
runtime: "nodejs22.x",
regions: ["iad1"], // Co-locate with your database
}; This is the mixed rendering strategy applied to compute location. Different routes have different performance characteristics. Deploy them accordingly.
My Approach
For most projects, here’s my default allocation:
| Layer | Where it runs | Why |
|---|---|---|
| Static assets (images, CSS, JS) | CDN edge | Always (this is just a CDN) |
| Middleware (auth, redirects, headers) | Edge functions | Stateless, latency-sensitive |
| API routes that query the database | Regional function | Co-located with the database |
| Prerendered pages | CDN edge | Static files, served globally |
| SSR pages with data | Regional function | Database access needed |
This isn’t dogma. If you’re using a globally distributed database, more of your stack can move to the edge. If all your users are in one country, the latency difference between edge and regional might not matter at all.
The key insight from choosing boring technology: don’t add edge complexity unless you have a measured latency problem that edge deployment solves. For a small business website with users in one metro area, a single-region deployment is simpler, cheaper, and fast enough.
When to Invest in Edge
Do invest when:
- Your users are globally distributed and latency matters (e-commerce, media, real-time apps)
- You have stateless middleware that runs on every request
- Your site serves prerendered content that benefits from global CDN caching
- You need geographic routing, A/B testing, or per-region customization
Don’t invest when:
- Your users are concentrated in one region
- Most of your routes need database access
- You’re optimizing prematurely; measure first
- The edge runtime doesn’t support your dependencies
Edge functions are a tool, not a destination. Use them where the latency math works out, keep everything else regional, and don’t let the marketing hype override the engineering reality.
Start with good performance fundamentals: fast Time to First Byte, optimized assets, server-rendered HTML. Then add edge execution only where it makes a measurable difference.