Choosing Between Netlify Functions and Edge Functions for Dynamic Code
A decision guide that compares Netlify Functions (AWS Lambda‑style) and Edge Functions (Deno‑compatible) for API endpoints, rewrites, and personalization, with a concrete validation path.
04 Jun 2026, 01:14 UTC

Decision: Where to run your dynamic code on Netlify
You need to add server‑side logic—such as an API endpoint, a request rewrite based on cookies or geography, or lightweight personalization. Netlify offers two runtimes: traditional Netlify Functions (AWS Lambda‑style) and Netlify Edge Functions (Deno‑compatible V8 isolates). The choice affects latency, cold‑start behavior, resource limits, and dependency support. This guide states the decision constraints, compares the options in a table, explains the trade‑offs, and shows a concrete validation path you can follow on your own site.
Constraints to consider
- Latency requirement: Does the logic need to run as close to the user as possible (e.g., geo‑based redirects, A/B test bucketing)?
- Compute intensity: Will the code run heavy npm packages, perform database queries, or execute long‑running tasks?
- Dependency ecosystem: Do you rely on Node built‑ins (fs, crypto, streams) or large npm packages that may not be Deno‑compatible?
- Plan limits: Are you on a free or paid Netlify plan? Background functions, scheduled functions, and edge‑function invocations have different gating.
- Framework integration: Are you using a framework adapter (Next.js, Remix, etc.) that may already place middleware on the edge?
Comparison table
| Aspect | Netlify Functions (Lambda‑style) | Netlify Edge Functions (Deno) |
|---|---|---|
| Invocation path | </.netlify/functions/<name> (or via _redirects) |
Matched by export const config = { path: '/<pattern>' } in the file |
| Runtime | AWS Lambda compatible, Node.js 18/20 or Go | Deno‑compatible V8 isolate, web‑standard APIs |
| Cold start | Possible, especially after idle periods; runs in a single AWS region | Near‑instant, starts at the Netlify edge node closest to the requester |
| CPU / time limits | Default synchronous limit ≈10 s; background functions (paid plans) can run up to 15 min | Per‑request CPU time cap (≈50 ms on free tier, higher on paid); no long‑running background mode |
| Memory | Configurable 128 MiB–1024 MiB (plan‑dependent) | Fixed low‑memory envelope (≈128 MiB) – heavy allocations may fail |
| Dependency support | Full npm, Node built‑ins, native modules (if compatible) | Web‑standard APIs; npm packages work only if they are Deno‑compatible or bundled; no Node built‑ins like fs |
| Typical use cases | REST APIs, webhooks, database access, image processing, long‑running jobs | Geo‑based redirects, cookie inspection, A/B testing, header injection, request/response rewriting, lightweight auth checks |
Trade‑off explanation
The core tension is latency versus capability. Edge Functions eliminate cold‑start delay because they execute at the Netlify edge, making them ideal for logic that must decide what to serve before any bytes are sent to the client (e.g., redirecting a user based on IP‑derived country). However, the edge runtime imposes strict CPU‑time caps and denies access to many Node APIs; if your function needs to query a PostgreSQL database or process a large payload, you will likely hit those limits or find that required npm packages cannot run.
Regular Functions, by contrast, run in a centralized AWS Lambda environment. They may incur a cold start (typically 100‑300 ms after idle) but provide a full Node.js environment, larger memory allowances, and the ability to run background tasks for up to 15 minutes on paid plans. This makes them the better fit for API endpoints that perform authentication, talk to external services, or execute any non‑trivial computation.
A common pattern is to use an Edge Function as a fast "gatekeeper": inspect the request, set cookies, or rewrite the URL, then forward to a Regular Function (or static asset) for the heavy work. This combines low‑latency routing with full backend capability.
Concrete implementation and validation
Below is a minimal example that demonstrates both runtimes and shows how you can measure latency and correctness.
1. Set up the repository
Run these commands in your project root (you need write access to the repo and the Netlify CLI installed).
# Clone or create a site
mkdir netlify-runtime-demo && cd netlify-runtime-demo
npm init -y
# Install netlify CLI locally (requires npm)
npm install --save-dev netlify-cli
2. Add a Regular Function
Create netlify/functions/hello.js:
exports.handler = async () => {
return {
statusCode: 200,
body: JSON.stringify({ message: 'hello from regular function', timestamp: Date.now() })
};
};
This function simply returns a JSON payload. It can use any npm package; for demonstration we keep it dependency‑free.
3. Add an Edge Function
Create netlify/edge-functions/geo.js:
export async function request(event) {
const country = event.geo?.country_code ?? 'unknown';
// Rewrite path based on country – example: /api/hello -> /api/hello?country=US
const url = new URL(event.request.url);
url.searchParams.set('country', country);
return {
// Forward to the regular function with the added query param
forwarding: url.pathname + url.search
};
}
export const config = { path: '/api/hello' };
This Edge Function runs on every request to /api/hello, reads the geo‑country supplied by Netlify, appends it as a query parameter, and forwards the request onward. No Node built‑ins are used; it relies only on the event object provided by the edge runtime.
4. Configure redirects (optional)
Add to netlify.toml if you want a cleaner URL:
[[redirects]]
from = "/hello"
to = "/api/hello"
status = 200
5. Test locally with netlify dev
Run the CLI from the project directory:
npx netlify dev
You should see output indicating both functions are loaded. Visit http://localhost:8888/hello (or the port shown) and inspect the response. You can also check the dev server logs to see which runtime processed the request.
6. Deploy to a preview branch for real‑world validation
Push the code to a branch and enable a Netlify deploy preview (or connect the repo to a new site). After the deploy finishes:
- Request the endpoint from multiple locations (you can use
curlwith a VPN or services like WebPageTest). Example:
curl -s https://<YOUR-SITE>.netlify.app/hello | jq .
Look for the country query param in the returned JSON; it should reflect the geo‑location of the requester.
- Go to Site settings → Functions → Invocations and verify that both the edge function (
geo.js) and the regular function (hello.js) appear. - In the function details, note the Init Duration (cold start) for the regular function and the near‑zero Duration for the edge function.
- Use the browser’s Network tab or a tool like
heyto record time to first byte (TTFB). Expect lower TTFB for the edge‑only path (if you bypass the regular function) and slightly higher TTFB when the request passes through both runtimes.
7. Verify limits and plan gating
In the Netlify dashboard, under Team settings → Functions & background, review:
- Synchronous function duration (should be ~10 s on free tier).
- Edge function CPU‑time per request (shown in the usage page).
- Whether background functions are available (requires a Pro plan or higher).
If your project needs longer‑running work, you must either upgrade the plan or move that work to a background function.
Practical way to check the result
After deploying, run this validation script (adjust the URL and number of runs):
#!/usr/bin/env bash
SITE="https://<YOUR-SITE>.netlify.app"
ENDPOINT="/hello"
RUNS=10
TOTAL=0
for i in $(seq 1 $RUNS); do
START=$(date +%s%3N)
RESPONSE=$(curl -s -o /dev/null -w "%{time_total}" "$SITE$ENDPOINT")
TOTAL=$(echo "$TOTAL + $RESPONSE" | bc)
done
AVG=$(echo "scale=3; $TOTAL / $RUNS" | bc)
echo "Average response time over $RUNS runs: $AVG seconds"
Run the script from a machine with internet access and bc installed. Compare the average time to a baseline where you temporarily disable the edge function (rename geo.js or change its path) and rerun the script. A noticeable increase confirms the edge function is adding latency; a negligible difference indicates the edge function is effectively passthrough.
When to choose each runtime
- Choose **Edge Functions** when:
- You need sub‑100 ms decision making based on geography, cookies, or headers.
- The logic is lightweight (mostly string manipulation, simple conditional routing).
- You want to avoid cold starts entirely.
- Choose **Regular Functions** when:
- You need to access databases, external APIs, or the file system.
- Your code depends on npm packages that are not Deno‑compatible.
- You require longer execution times (background jobs, webhooks with retries).
Many applications benefit from a hybrid approach: let the edge function handle fast request inspection and rewriting, then delegate heavy lifting to a regular function. Validate both paths with the steps above before committing to a production design.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.