Choosing between Edge Runtime and Node Runtime changes your app's latency, cost, and API compatibility, and most full-stack developers pick wrong by defaulting to Node. This guide breaks down the real trade-offs with working code, so you can decide based on your actual workload—not hype.
Why Edge Runtime vs Node Runtime Matters (and When to Skip It)
Here's my take: if you're building a CRUD app with a traditional database, skip the edge entirely. The Edge Runtime shines for globally distributed, read-heavy workloads—think API gateways, personalization, or auth checks. Node Runtime remains the workhorse for CPU-bound tasks, long-running processes, and anything needing the full Node.js API surface.
The pain point? Most developers deploy to the edge, hit a missing Node API, and burn hours debugging. I've seen teams rewrite perfectly good Express apps into edge-compatible handlers only to gain 50ms while losing half their npm ecosystem. Don't be that team.
Getting Started with Edge Runtime vs Node Runtime
Let's set up both runtimes with minimal code. First, a Node Runtime handler using Express:
// server.ts - Node Runtime
import express from 'express';
import { PrismaClient } from '@prisma/client';
const app = express();
const prisma = new PrismaClient();
app.get('/api/users/:id', async (req, res) => {
const user = await prisma.user.findUnique({
where: { id: req.params.id }
});
res.json(user);
});
app.listen(3000);
Now the same endpoint on Edge Runtime (Vercel-style):
// edge.ts - Edge Runtime
export const config = { runtime: 'edge' };
export default async function handler(req: Request) {
const { searchParams } = new URL(req.url);
const id = searchParams.get('id');
// Edge has no Node APIs - use fetch directly
const user = await fetch(`https://api.internal.com/users/${id}`);
return new Response(JSON.stringify(await user.json()), {
headers: { 'Content-Type': 'application/json' }
});
}
Notice the difference: Edge uses the Web Fetch API, not Node's http module. That's the core shift.
Core Edge Runtime vs Node Runtime Concepts Every Developer Should Know
1. The Web Standard API vs Node's Built-ins
Edge Runtime implements the WHATWG standard APIs—fetch, Request, Response, URL. Node has its own http, https, and stream modules. Your code must target one or the other.
// Node-specific code that breaks on Edge
import fs from 'node:fs';
const data = fs.readFileSync('./config.json');
// Edge-compatible alternative
const response = await fetch('./config.json');
const data = await response.json();
2. Execution Location and Cold Starts
Node runs on a fixed server region. Edge runs on a CDN node closest to the user. This is the biggest win for latency but introduces cold start variability.
// Edge - runs on the nearest CDN node
export default async function handler() {
const start = Date.now();
const res = await fetch('https://api.example.com/data');
return new Response(`Took ${Date.now() - start}ms`);
}
3. Memory and Runtime Constraints
Edge limits you to ~128MB memory and a 30-second execution cap. Node gives you 512MB+ and unlimited time. If you're processing images or running heavy computations, Node wins.
// This will timeout on Edge
export default async function handler() {
const bigArray = new Array(10000000).fill('data');
// 10M elements - OOM on Edge
return new Response('done');
}
Common Edge Runtime vs Node Runtime Mistakes and How to Fix Them
Mistake 1: Using Node-specific packages on Edge. You'll see Cannot find module 'fs' errors. Fix: check your dependencies for Node built-ins and replace with edge-compatible libraries like unstorage or @vercel/kv.
Mistake 2: Assuming Edge is always faster. If your app talks to a database in a single region, Edge adds a hop. Fix: measure with real traffic. Use performance.now() in both runtimes and compare p95 latency.
Mistake 3: Ignoring the 30-second timeout. Long-running background jobs fail silently on Edge. Fix: move them to a separate Node service or use a queue system like BullMQ.
// Bad - runs over 30s
export default async function handler() {
for (let i = 0; i < 1000000; i++) {
await processItem(i); // slow
}
}
// Good - offload to queue
export default async function handler() {
await queue.add('process', { items: [1, 2, 3] });
return new Response('queued');
}
When Should You Use Edge Runtime vs Node Runtime?
Use Edge Runtime when:
- Your API is read-heavy and globally accessed (like a content API)
- You need sub-100ms response times across continents
- Your data is cached or served from a global CDN
Use Node Runtime when:
- You're building CRUD apps with a centralized database
- You need file system access or child processes
- You're using Node-specific packages like
pgormongodbdirectly - You have long-running tasks or WebSocket connections
The rule of thumb: if your app is I/O-bound and stateless, go Edge. If it's stateful or compute-heavy, stay Node. For most full-stack projects, a hybrid approach works best—edge for public APIs, Node for admin panels and background jobs.
Edge Runtime vs Node Runtime in Production
Tip 1: Abstract your runtime layer. Write a wrapper that works on both, so you can migrate services without rewriting everything.
// runtime-agnostic wrapper
export async function getData(url: string) {
if (typeof process !== 'undefined' && process.versions?.node) {
// Node path
const http = await import('node:http');
// ... Node-specific logic
} else {
// Edge path
return fetch(url);
}
}
Tip 2: Cache aggressively on Edge. Use Cache-Control headers and a global KV store to avoid hitting origin servers. I've seen 80% cache hit rates on edge APIs, which makes the latency win real.
Tip 3: Monitor both runtimes separately. Set up alerts for edge cold starts and Node memory usage. They fail differently, so don't treat them as one system.
Final takeaway: Write a proof-of-concept with both runtimes for your heaviest endpoint, measure p50 and p95 latency under real traffic, and pick the one that wins—then apply that pattern to your whole stack.