Choosing Between Netlify Edge Functions and Serverless Functions for Low‑Latency APIs
A decision guide that compares Edge Functions (Deno) and Serverless Functions (Node.js) for a read‑heavy Jamstack API, shows concrete code examples, and lists validation steps.
02 Jan 2026, 10:32 UTC

Decision and Constraints
When you need a read‑heavy API behind a Jamstack front end, the primary goal is to keep time‑to‑first‑byte (TTFB) under 50 ms for users everywhere while staying inside Netlify’s platform limits. The decision is whether to implement the API as an Edge Function (Deno‑based) or a Serverless Function (Node.js‑based). The constraints that shape this choice are:
- Sub‑50 ms TTFB for global users
- No persistent connections; read‑only data access
- Edge bundle ≤ 50 MB, Serverless deploy package ≤ 50 MB
- Edge CPU limit 50 ms per invocation, Serverless memory up to 1024 MB
- Need to use Node libraries for heavier workloads
Option Comparison
| Feature | Edge Functions | h>Serverless Functions|
|---|---|---|
| Runtime | Deno (web‑standard APIs) | Node.js (AWS Lambda) |
| Bundle size limit | 50 MB | 50 MB (deploy package) |
| Memory | 1 MB response limit, CPU 50 ms | Up to 1024 MB configurable |
| Cold start | Near‑instant (edge nodes) | 100‑500 ms typical |
| Latency (global) | Low, runs close to user | Higher outside primary region |
| Node ecosystem | Limited to web std APIs; no native npm | Full Node.js/npm support |
| Max response size | 1 MB | Limited by gateway (≈ 1 MB typical) |
| Execution time limit | 50 ms CPU wall time | 10 s sync, 15 min async |
Trade‑offs
Edge Functions win on latency and cold‑start avoidance, making them ideal for small, read‑only payloads that must be served globally with sub‑50 ms TTFB. They are cost‑effective at high request volumes because you pay only for execution time and there is no persistent compute overhead. The downsides are the Deno runtime (no Node built‑ins or native addons), a strict 50 ms CPU budget, and a 1 MB response ceiling.
Serverless Functions win on compatibility with existing Node SDKs, larger memory and timeout budgets, and the ability to run longer‑running or compute‑intensive workloads. The trade‑off is higher latency for users far from the function’s region and a cold‑start penalty that can break the sub‑50 ms goal for infrequent traffic.
Implementation Pattern
Edge Function (Deno)
Place the file at netlify/edge-functions/api.ts. Use only web‑standard APIs; fetch data from an origin cache or Netlify KV.
export default async function handler(request) {
const url = new URL(request.url);
const key = url.searchParams.get('id') ?? 'default';
// Example: read from Netlify KV (requires KV namespace binding)
const value = await NETLIFY_KV.get(key);
if (value === null) {
// fallback to origin cache
const originResp = await fetch(`https://example.com/api/${key}`);
const data = await originResp.json();
return new Response(JSON.stringify(data), {
headers: { 'content-type': 'application/json', 'cache-control': 's-maxage=60' },
});
}
return new Response(value, {
headers: { 'content-type': 'application/json', 'cache-control': 's-maxage=60' },
});
}
Keep the bundle under 50 MB by avoiding large dependencies; if you need a library, choose one that provides an ES‑module build compatible with Deno.
Serverless Function (Node.js)
Place the file at netlify/functions/api.js. You can use standard Node packages such as node-fetch or axios.
const fetch = require('node-fetch');
export async function handler(event, context) {
const { id } = event.queryStringParameters || {};
const key = id ?? 'default';
try {
const originResp = await fetch(`https://example.com/api/${key}`);
if (!originResp.ok) {
return { statusCode: originResp.status, body: originResp.statusText };
}
const data = await originResp.json();
return {
statusCode: 200,
body: JSON.stringify(data),
headers: { 'content-type': 'application/json', 'cache-control': 's-maxage=60' },
};
} catch (err) {
return { statusCode: 502, body: JSON.stringify({ error: err.message }) };
}
}
Configure build settings in netlify.toml if you need to adjust the timeout or memory:
[functions]
directory = "netlify/functions"
node_bundler = "esbuild"
# optional: increase memory to 1024 MB
# memory = 1024
Validation and Practical Checks
- Deploy a minimal Edge Function and a minimal Serverless Function that return static JSON.
- From two geographic locations (e.g., using a VPN or a service like
curlon a VPS), measure TTFB:
curl -s -w "%{time_starttransfer}\n" https://<site-name>.netlify.app/.netlify/functions/api?id=test
curl -s -w "%{time_starttransfer}\n" https://<site-name>.netlify.app/api/api?id=test
hey or wrk) with a concurrency of 10 for 30 seconds and observe:- Edge Function: steady latency, minimal variance.
- Serverless Function: higher latency on the first request after idle (cold start), then lower latency for warm requests.
Limitations and How to Verify
Edge Functions cannot use Node‑specific modules or native addons; any library must be rewritten to use only web‑standard APIs or be available as an ES‑module Deno build. The 50 ms CPU limit means heavy computation will be throttled or cause a 502 response. Serverless Functions incur cost proportional to execution time and memory, so long‑running or memory‑intensive tasks can become expensive.
To verify you remain within limits:
- After each deploy, view the Functions tab in the site dashboard; it lists bundle size and runtime.
- Set up a monitoring alert (via Netlify integrations or a third‑party service) that triggers if the 95th‑percentile TTFB exceeds 50 ms for Edge Functions.
- For Serverless Functions, check the Invocations and Duration metrics; a spike in duration often indicates a cold start.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.