The vercel ai sdk edge vs node.js runtime decision shapes everything from cold-start latency to which libraries you can import. Both runtimes support streaming responses via streamText and streamObject, but they diverge sharply on execution model, ecosystem access, and cost profile. This comparison breaks down the trade-offs so you can pick the right runtime for your workload without guessing.
Execution model and cold starts
Edge runs on V8 isolates — not full Node.js processes. Isolates start in single-digit milliseconds, but they share a global isolate pool across invocations. That means no process global, no fs module, and no native addons. Node.js on Vercel runs in a traditional serverless container: cold starts around 100–300 ms for a minimal function, but you get the complete Node.js API surface.
For AI streaming, the cold-start difference matters most at low traffic. An Edge function serving a chat endpoint stays warm more aggressively because isolates are cheaper to keep alive. Node.js functions scale to zero more aggressively on the Hobby and Pro plans, so the first request after idle pays the container startup tax.
// Edge runtime — app/api/chat/route.ts
export const runtime = 'edge'
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4o-mini'),
messages,
})
return result.toDataStreamResponse()
}
// Node.js runtime — app/api/chat/route.ts
export const runtime = 'nodejs'
import { streamText } from 'ai'
import { openai } from '@ai-sdk/openai'
// Node-only imports work here
import { readFileSync } from 'fs'
import { resolve } from 'path'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4o-mini'),
messages,
})
return result.toDataStreamResponse()
}
Streaming behavior and backpressure
Both runtimes implement the same ReadableStream contract from the AI SDK. The difference shows up under load. Edge isolates have a hard 128 MB memory limit and a 30-second max execution time (Pro plan: 60 s). Node.js functions get 1 GB (Hobby) or 3 GB (Pro) and up to 800 seconds max duration.
When streaming large model responses — think 8k+ output tokens — Edge can hit the memory ceiling if you buffer chunks server-side. The AI SDK’s toDataStreamResponse() streams directly to the client without buffering, so this rarely bites in practice. But if you transform the stream (e.g., inject citations, run a moderation pass per chunk), Node.js gives you more headroom.
// Transforming a stream — safe on both, but Node.js has more margin
import { streamText, smoothStream } from 'ai'
const result = await streamText({
model: openai('gpt-4o'),
messages,
experimental_transform: smoothStream({ chunking: 'word' }),
})
Tool calling and structured outputs
Tool calling works identically on both runtimes — the AI SDK handles the loop. Where Node.js wins: you can call into heavy libraries inside tools. PDF parsing, image processing, database drivers with native bindings, sharp, pdf-parse, playwright — all require Node.js. Edge supports a growing subset of npm packages (ESM-only, no native deps), but anything touching crypto, fs, or native modules fails at deploy time.
// Node.js only — tool that uses a native dependency
import { streamText, tool } from 'ai'
import { z } from 'zod'
import pdf from 'pdf-parse' // native dep, Node.js only
const extractPdfText = tool({
parameters: z.object({ buffer: z.instanceof(Buffer) }),
execute: async ({ buffer }) => {
const data = await pdf(buffer)
return data.text
},
})
Local development parity
vercel dev emulates both runtimes locally, but Edge emulation uses workerd (the Cloudflare Workers runtime) while Node.js runs actual Node.js. This creates subtle mismatches: crypto.subtle behaves differently, Request/Response implementations diverge, and some Web APIs polyfill incompletely in workerd.
Node.js local dev is higher fidelity. If your CI runs integration tests against a real Vercel preview deployment, the gap narrows — but you still catch Edge-specific failures later in the pipeline.
Ecosystem and middleware
Edge middleware runs on every request before your route handler. It’s useful for auth checks, bot detection, or rewriting — but it shares the same isolate limits. You cannot import Node.js-only packages in middleware. Node.js has no equivalent middleware layer; you handle cross-cutting concerns in route handlers or a custom server (which defeats serverless benefits).
For AI workloads, the practical impact is small. Most auth lives in a separate Edge middleware anyway, and the AI route itself picks its runtime independently.
Cost model
Vercel bills Edge and Node.js differently:
| Dimension | Edge | Node.js |
|---|---|---|
| Invocation cost | $0.50 / million invocations (after free tier) | Included in function duration |
| Duration billing | GB-hours × $0.000016 | GB-hours × $0.000024 |
| Free tier | 1M invocations + 100 GB-hours/mo | 100 GB-hours/mo |
| Max memory | 128 MB | 1 GB (Hobby) / 3 GB (Pro) |
| Max duration | 30 s (Hobby) / 60 s (Pro) | 60 s (Hobby) / 800 s (Pro) |
For high-frequency, low-latency chat (many short invocations), Edge wins on invocation pricing. For long-running streams or heavy tool use, Node.js duration billing is cheaper per GB-hour but you pay for more memory. A 512 MB Node.js function running 10 seconds costs the same GB-hours as a 128 MB Edge function running 40 seconds — but the Node.js function can actually complete the work.
Bundle size and deployment
Edge enforces a 1 MB compressed bundle limit (3 MB uncompressed). The AI SDK core is ~200 KB gzipped; adding @ai-sdk/openai, zod, and a few utilities pushes you to ~400–600 KB. You have headroom, but large dependencies (e.g., langchain, heavy ORMs) will break the build.
Node.js has a 50 MB limit. You can bundle Prisma, full ORMs, or ML libraries without webpack gymnastics. The trade-off: larger bundles increase cold-start time because the container must download and extract them.
# Check your Edge bundle size locally
npx @vercel/nft --output bundle.json app/api/chat/route.ts
# Look at "size" in the output — must be < 1,048,576 bytes gzipped
Limits summary
| Limit | Edge | Node.js (Pro) |
|---|---|---|
| Memory | 128 MB | 3 GB |
| Max duration | 60 s | 800 s |
| Bundle size (gzipped) | 1 MB | 50 MB |
| Concurrent invocations | 1000 (soft) | 1000 (soft) |
| Request body size | 1 MB | 5 MB |
| Response streaming | Yes | Yes |
| WebSocket support | No | No (use Pusher/Ably) |
| Native addons | No | Yes |
fs, crypto, child_process |
No | Yes |
Debugging and observability
Edge logs appear in Vercel’s dashboard with the same UI as Node.js, but stack traces can be less complete because workerd strips some frames. console.log works, but you lose async_hooks-based context propagation (e.g., cls-rtracer for request IDs). Node.js gives you full Node inspector support — you can attach node --inspect locally and debug with Chrome DevTools.
For production debugging, both runtimes integrate with Vercel’s Log Drains (Datadog, Logtail, Axiom). The AI SDK’s onFinish callback works identically on both, so you can emit custom telemetry per completion.
// Works on both runtimes
const result = await streamText({
model: openai('gpt-4o'),
messages,
onFinish: async ({ usage, finishReason }) => {
await analytics.track('ai_completion', {
tokens: usage.totalTokens,
finishReason,
runtime: process.env.NEXT_RUNTIME, // 'edge' or 'nodejs'
})
},
})
When to choose Edge
- High-frequency, low-latency chat — many short requests, sub-100ms cold starts matter
- Global distribution priority — Edge runs in 35+ regions; Node.js runs in fewer primary regions
- Simple request/response — no heavy tooling, no native deps, no large bundle
- Middleware-heavy auth — you’re already running Edge middleware, keep the stack uniform
- Cost optimization at scale — millions of invocations where $0.50/M beats duration billing
When to choose Node.js
- Tool calling with native deps — PDF, images, audio, video,
sharp,canvas,playwright - Long-running streams — 60+ second model responses, multi-step agent loops
- Heavy libraries — Prisma, Drizzle with full relations, LangChain, vector DB clients
- Local dev fidelity — you need
crypto,fs,child_processto behave identically in CI - Higher memory ceiling — buffering, transformation, or large context windows
- Team familiarity — your engineers know Node.js debugging tools, not
workerdquirks
Hybrid approach (recommended for most teams)
Run your chat endpoint on Edge for fast cold starts and global latency. Offload heavy tools to a separate Node.js route (or a background job via Inngest/Trigger.dev/QStash) that the Edge route calls via HTTP. This keeps the hot path fast while giving you Node.js power where you need it.
// Edge route — fast path, calls Node.js tool endpoint
export const runtime = 'edge'
import { streamText, tool } from 'ai'
import { openai } from '@ai-sdk/openai'
const analyzeDocument = tool({
parameters: z.object({ url: z.string().url() }),
execute: async ({ url }) => {
const res = await fetch(`${process.env.NODEJS_API_URL}/analyze`, {
method: 'POST',
body: JSON.stringify({ url }),
})
return res.json()
},
})
export async function POST(req: Request) {
const { messages } = await req.json()
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools: { analyzeDocument },
})
return result.toDataStreamResponse()
}
Verdict
Default to Edge for new AI streaming endpoints. The cold-start advantage, global footprint, and lower invocation cost align with typical chat workloads. You’ll hit the limits (bundle size, native deps, 60 s max) only when you add complexity — and that’s the right time to migrate that specific route to Node.js.
Choose Node.js first if you already know you need native dependencies, long durations, or heavy libraries. Don’t preemptively optimize for Edge if your toolchain fights you.
Use the hybrid pattern when a single feature needs both: keep the streaming UX on Edge, push the heavy lifting to Node.js. The AI SDK’s tool calling makes this clean — the model doesn’t care where the tool executes.