Back to Blog

Edge Functions vs Serverless Functions: When to Use Each

Edge Functions vs Serverless Functions: When to Use Each

Every time we start a new project, one of the first architectural decisions we make is where the server-side logic runs. Not whether it runs on a server — that debate is settled — but where geographically, and under what execution model.

The two dominant options in 2026 are serverless functions (AWS Lambda, Vercel Functions, Google Cloud Functions) and edge functions (Cloudflare Workers, Supabase Edge Functions, Vercel Edge Functions, Deno Deploy). They sound similar. They both run your code without managing servers. But they behave very differently in practice, and choosing wrong can cost you months of refactoring.

We have shipped projects using both. MindHyv runs critical API routes on Supabase Edge Functions. Trackelio uses Vercel Serverless Functions for webhook processing. Here is what we have learned.

The Core Difference

Serverless functions run in a single region (or a few regions you configure). Your function code lives on a server in, say, us-east-1. When a user in Manila makes a request, that request travels across the Pacific, hits your function, and the response travels back. Round-trip latency: 200-400ms before your code even starts executing.

Edge functions run on a distributed network of hundreds of locations worldwide. When that same user in Manila makes a request, it hits a node in Singapore or Hong Kong — maybe 20-50ms away. The function executes there, and the response comes back fast.

That is the pitch. Here is the reality.

Latency: The Numbers

We benchmarked a simple JSON API endpoint across both models. The function reads from a KV store (Cloudflare KV for edge, DynamoDB for Lambda) and returns a transformed response.

Cold start latency (p50):

PlatformCold StartWarm Response
AWS Lambda (Node.js, us-east-1)250-800ms5-15ms
Vercel Serverless (Node.js)200-500ms8-20ms
Cloudflare Workers0-5ms1-3ms
Supabase Edge Functions (Deno)10-50ms2-8ms
Vercel Edge Functions0-5ms1-5ms

The cold start difference is dramatic. Lambda cold starts are a well-documented problem. You can mitigate them with provisioned concurrency, but that costs money and defeats the “pay per invocation” benefit.

Edge functions essentially eliminate cold starts. Cloudflare Workers use V8 isolates instead of containers — they spin up in under 5ms. Supabase Edge Functions use Deno’s runtime, which is heavier but still far lighter than a full Node.js container.

But here is the catch: those warm response times assume your function is doing simple work. The moment you need to talk to a database, the latency equation changes completely.

Cloud serverless functions processing requests across distributed infrastructure

The Database Problem

Your edge function runs in 300+ locations. Your database runs in one. Maybe two if you have read replicas.

// This edge function runs in Singapore
// But your PostgreSQL database is in us-east-1
export default async function handler(req: Request) {
  // This query adds 150-300ms of network latency
  const { data, error } = await supabase
    .from('products')
    .select('*')
    .eq('category', 'electronics')
    .limit(20);

  return new Response(JSON.stringify(data), {
    headers: { 'Content-Type': 'application/json' },
  });
}

You saved 200ms by running at the edge. Then you lost 200ms calling back to your database. Net benefit: roughly zero.

This is the single most important thing to understand about edge functions. They are only faster when:

  1. The function does not need a database call (auth token validation, redirects, A/B testing, header manipulation)
  2. The data source is also distributed (KV stores, edge caches, replicated databases)
  3. You can cache the database response at the edge

For MindHyv, we use Supabase Edge Functions for webhook signature validation and request routing. These operations do not touch the database — they validate an HMAC signature and forward the request. Perfect edge use case. The actual data processing happens in serverless functions colocated with the database.

Runtime Limitations

Edge functions run in constrained environments. This matters more than most teams realize until they hit the walls.

Cloudflare Workers limitations:

  • 128MB memory limit (recently raised, was 64MB)
  • 30 second CPU time limit (not wall clock — but still limiting)
  • No native Node.js APIs (no fs, no child_process, no net)
  • Limited npm package compatibility
  • No WebSocket initiation (can handle, but not initiate)

Supabase Edge Functions (Deno) limitations:

  • 150MB memory limit
  • 60 second wall clock timeout
  • Deno runtime, not Node.js — most npm packages work via npm: specifiers, but not all
  • No persistent connections between invocations

AWS Lambda advantages:

  • Up to 10GB memory
  • 15 minute execution timeout
  • Full Node.js runtime with all native APIs
  • Any npm package works
  • Persistent /tmp storage (512MB-10GB)
  • VPC access for private resources

Here is a real example. We needed to generate PDF invoices for LancerSpace. The PDF library we use (puppeteer-based) requires a full headless browser. That is a non-starter on edge functions. It needs 500MB+ of memory and takes 3-8 seconds to render. We run it on Lambda with 1GB memory and a 30-second timeout.

// This ONLY works on a traditional serverless function
// Edge functions cannot run headless browsers
import puppeteer from 'puppeteer-core';
import chromium from '@sparticuz/chromium';

export async function generateInvoicePDF(invoiceData: InvoiceData): Promise<Buffer> {
  const browser = await puppeteer.launch({
    args: chromium.args,
    executablePath: await chromium.executablePath(),
    headless: chromium.headless,
  });

  const page = await browser.newPage();
  await page.setContent(renderInvoiceHTML(invoiceData));
  const pdf = await page.pdf({ format: 'A4' });
  await browser.close();

  return Buffer.from(pdf);
}

Try doing that on a Cloudflare Worker. You cannot.

Cost Comparison

Both models charge per invocation, but the pricing structures differ.

Cloudflare Workers (paid plan):

  • $5/month includes 10 million requests
  • $0.50 per additional million requests
  • No charge for execution duration

AWS Lambda:

  • $0.20 per million requests
  • $0.0000166667 per GB-second of compute
  • Free tier: 1M requests + 400,000 GB-seconds per month

Supabase Edge Functions:

  • Included in Supabase Pro plan ($25/month)
  • 2 million invocations included
  • $2 per million additional invocations

For high-volume, low-compute workloads (API routing, auth checks, redirects), edge functions are cheaper. For compute-heavy, lower-volume workloads (PDF generation, image processing, data pipelines), serverless functions give you more flexibility per dollar.

Global CDN network nodes distributing content across worldwide locations

When to Use Edge Functions

Use edge functions when:

1. You are doing request-level logic that does not need a database.

// Perfect edge function: A/B test routing
export default function handler(req: Request): Response {
  const userId = req.headers.get('x-user-id') || crypto.randomUUID();
  const bucket = parseInt(userId.slice(-2), 16) % 100;

  const variant = bucket < 50 ? 'control' : 'treatment';

  return new Response(null, {
    status: 302,
    headers: {
      'Location': variant === 'control' ? '/pricing' : '/pricing-v2',
      'Set-Cookie': `ab_variant=${variant}; Path=/; HttpOnly`,
    },
  });
}

2. You are validating authentication tokens.

JWT validation is pure computation — no database needed. We run auth middleware on the edge for several projects, rejecting invalid tokens before they ever reach our API servers.

import { jwtVerify } from 'jose';

export default async function authMiddleware(req: Request): Promise<Response> {
  const token = req.headers.get('Authorization')?.replace('Bearer ', '');

  if (!token) {
    return new Response('Unauthorized', { status: 401 });
  }

  try {
    const { payload } = await jwtVerify(
      token,
      new TextEncoder().encode(Deno.env.get('JWT_SECRET'))
    );

    // Token valid — forward to origin with user context
    const headers = new Headers(req.headers);
    headers.set('x-user-id', payload.sub as string);
    headers.set('x-user-role', payload.role as string);

    return fetch(req.url.replace('edge.', 'api.'), {
      method: req.method,
      headers,
      body: req.body,
    });
  } catch {
    return new Response('Invalid token', { status: 401 });
  }
}

3. You are serving data from an edge-compatible store.

Cloudflare KV, Durable Objects, D1 (SQLite at the edge), or cached responses from your origin.

4. You are handling webhooks that need fast acknowledgment.

Webhook providers often have tight timeout windows (5-15 seconds). An edge function can acknowledge the webhook instantly, then forward the payload to a queue for processing.

When to Use Serverless Functions

Use serverless functions when:

  1. Your logic is compute-heavy. Image processing, PDF generation, video transcoding, ML inference — anything that needs memory and CPU time.

  2. You need the full Node.js ecosystem. Native modules, file system access, child processes, or packages that do not work in edge runtimes.

  3. Your function talks to a single-region database. If your database is in us-east-1, put your function in us-east-1. The edge buys you nothing here.

  4. You need long execution times. Batch jobs, report generation, data migrations — anything over 30-60 seconds.

  5. You need VPC access. Private databases, internal services, resources behind a firewall.

Cloud computing infrastructure with server racks in a data center

The Hybrid Approach

Most of our production architectures use both. Here is the pattern we have settled on:

User Request
    |
    v
[Edge Function] -- auth validation, rate limiting, routing
    |
    v
[CDN Cache] -- serve cached responses when possible
    |
    v
[Serverless Function] -- business logic, database queries
    |
    v
[Database] -- PostgreSQL in a single region

The edge layer handles the fast, stateless work. The serverless layer handles the heavy lifting. We cache aggressively between them. This is the approach we used for Trackelio — the feedback widget script is served from the edge, the actual feedback submission hits a serverless function colocated with the database.

For more on how we structure caching between these layers, see our post on caching strategies for web apps.

Choosing Your Platform

If you are already on Supabase (like we often are), Supabase Edge Functions are the obvious starting point. They are deeply integrated with the Supabase ecosystem, and the Deno runtime is capable enough for most edge use cases.

If you need maximum edge performance and are willing to work within tighter constraints, Cloudflare Workers are the fastest option. Their V8 isolate model is genuinely impressive.

If you need flexibility and do not care about edge latency, AWS Lambda or Vercel Serverless Functions give you the most headroom. We reach for these when the function is doing real work — not just routing.

The worst choice is picking one model for everything. Edge functions are not a replacement for serverless. Serverless is not obsolete because edge exists. They solve different problems, and the best architectures use both where each makes sense.

If you are building something and trying to figure out where your server logic should run, reach out at hello@threshline.com. We have shipped enough of these to know when the edge is a real win and when it is just added complexity.