TechLead
Lesson 11 of 22
5 min read
Performance Engineering

Edge Computing

Deploy code at the edge for ultra-low latency using Edge Functions, Middleware, and edge-first architecture patterns

What Is Edge Computing?

Edge computing runs your code on servers geographically distributed close to your users, rather than in a centralized data center. Instead of a request traveling thousands of miles to a single origin server, it is handled by the nearest edge node — often within 50ms. This dramatically reduces latency for dynamic content.

Edge vs Origin

  • Origin server: Single region, 100-300ms latency for distant users. Full Node.js runtime.
  • Edge function: 200+ locations globally, 10-50ms latency. Lightweight V8 runtime.
  • CDN cache: Static assets at the edge, 5-20ms. No compute, just serving cached files.

Next.js Edge Middleware

// middleware.ts — Runs at the edge before every request
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const { pathname, searchParams } = request.nextUrl;
  const country = request.geo?.country || 'US';
  const city = request.geo?.city || 'Unknown';

  // 1. Geo-based routing for lower latency
  if (pathname === '/store') {
    const localizedPath = country === 'DE' ? '/store/de' :
                          country === 'JP' ? '/store/jp' : '/store/us';
    return NextResponse.rewrite(new URL(localizedPath, request.url));
  }

  // 2. A/B testing at the edge (no client-side flicker)
  const bucket = request.cookies.get('ab-bucket')?.value;
  if (!bucket && pathname === '/') {
    const newBucket = Math.random() < 0.5 ? 'control' : 'variant';
    const response = NextResponse.rewrite(
      new URL(`/${newBucket}`, request.url)
    );
    response.cookies.set('ab-bucket', newBucket, { maxAge: 60 * 60 * 24 * 30 });
    return response;
  }

  // 3. Bot detection and rate limiting
  const userAgent = request.headers.get('user-agent') || '';
  if (isBot(userAgent) && pathname.startsWith('/api')) {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
  }

  // 4. Add performance headers
  const response = NextResponse.next();
  response.headers.set('X-Edge-Location', city);
  response.headers.set('Server-Timing', `edge;desc="Edge Processing"`);
  return response;
}

function isBot(ua: string): boolean {
  return /bot|crawler|spider|scraper/i.test(ua);
}

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

Edge API Routes

// app/api/search/route.ts — Edge API route
import { NextRequest, NextResponse } from 'next/server';

export const runtime = 'edge'; // Run on edge network

export async function GET(request: NextRequest) {
  const query = request.nextUrl.searchParams.get('q');
  if (!query) {
    return NextResponse.json({ results: [] });
  }

  // Use edge-compatible data sources
  // KV store, edge database, or cached API responses
  const startTime = performance.now();

  const results = await searchFromEdgeStore(query);

  const duration = performance.now() - startTime;

  return NextResponse.json(
    { results, meta: { query, count: results.length } },
    {
      headers: {
        'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=600',
        'Server-Timing': `search;dur=${duration.toFixed(1)}`,
        'CDN-Cache-Control': 'public, max-age=300',
      },
    }
  );
}

async function searchFromEdgeStore(query: string): Promise<any[]> {
  // In a real app, this would query an edge database like
  // Turso, PlanetScale, Neon, or a KV store like Vercel KV
  const response = await fetch(`https://search-api.example.com/search?q=${encodeURIComponent(query)}`, {
    headers: { Authorization: `Bearer ${process.env.SEARCH_API_KEY}` },
    next: { revalidate: 300 },
  });
  return response.json();
}

Edge-Compatible Data Patterns

// Edge functions have limitations: no Node.js APIs, limited execution time
// Use edge-compatible alternatives:

// 1. KV Store for fast reads (Vercel KV, Cloudflare KV)
import { kv } from '@vercel/kv';

export const runtime = 'edge';

export async function GET(request: NextRequest) {
  const key = request.nextUrl.searchParams.get('key');
  const cached = await kv.get(key);
  if (cached) return NextResponse.json(cached);

  const data = await fetchFromOrigin(key);
  await kv.set(key, data, { ex: 3600 }); // Cache for 1 hour
  return NextResponse.json(data);
}

// 2. Edge-compatible database (Turso/libSQL)
// Ultra-low latency reads from replicas at the edge
import { createClient } from '@libsql/client/web';

const db = createClient({
  url: process.env.TURSO_DATABASE_URL!,
  authToken: process.env.TURSO_AUTH_TOKEN!,
});

export async function getProduct(id: string) {
  const result = await db.execute({
    sql: 'SELECT * FROM products WHERE id = ?',
    args: [id],
  });
  return result.rows[0];
}

Edge Computing Best Practices

  • Use edge for latency-sensitive paths: Authentication, redirects, A/B tests, geo-routing
  • Cache at the edge: Use CDN-Cache-Control headers for edge-level caching
  • Keep functions lightweight: Edge functions have size and execution time limits
  • Use edge-compatible databases: Turso, PlanetScale, Neon for global read replicas
  • Measure edge vs origin: Use Server-Timing headers to compare latency

Continue Learning