MinhVo

Minh Vo

rss feed

Slaying code & making it lit fr fr πŸ”₯ tagline

Hey there πŸ‘‹ I'm an AI Engineer with 7 years of experience building scalable web and mobile applications. Currently at Neurond AI (May 2025 β€” present), architecting an Enterprise AI Assistant Platform with multi-tenant RAG on pgvector, multi-provider LLM orchestration, and Azure-native infrastructure. Previously spent 5+ years at SNAPTEC (Sep 2019 β€” Apr 2025), leading SaaS themes, admin dashboards, and e-commerce platforms β€” earned the Hero of the Year award in 2021. I specialize in TypeScript, React, Next.js, and AI-Native engineering with Claude Code and Cursor.bio

Back to blogs

Edge Computing: Architecture and Frameworks

Build edge applications: edge functions, edge databases, and CDN compute.

Edge ComputingArchitectureCloudPerformance

By MinhVo

Introduction

The centralized cloud computing model that dominated the 2010s is giving way to a more distributed paradigm. Edge computing pushes computation closer to users β€” to CDN nodes, regional data centers, and even the user's own device. This architectural shift reduces latency from hundreds of milliseconds to single digits, enables compliance with data sovereignty regulations, and opens entirely new categories of applications that were impossible under the traditional cloud model.

Edge computing is not about replacing the cloud. It is about extending the cloud's reach to every corner of the planet. Cloudflare Workers run code in 300+ cities worldwide. Vercel Edge Functions execute at the nearest point of presence. Deno Deploy runs JavaScript in 35 regions. The result is that a user in Tokyo and a user in London both get sub-50ms response times, regardless of where the origin server lives.

In this guide, we'll explore the architecture of edge computing, the frameworks that power it, and the design patterns that make it work. You'll learn when to use edge functions versus traditional serverless, how to choose between edge databases, and how to build applications that leverage edge computing for real performance gains.

Global edge computing network visualization

Understanding Edge Computing: Core Concepts

Edge computing is a distributed computing paradigm that brings computation and data storage closer to the sources of data. Instead of sending every request to a centralized data center, edge computing processes requests at the network edge β€” as close to the user as possible.

The Edge Computing Spectrum

Edge computing exists on a spectrum from CDN-level caching to full application execution:

Level 1 β€” Static Edge: CDN caches static assets (HTML, CSS, images) at edge locations. This is the simplest form and is used by virtually every modern website.

Level 2 β€” Dynamic Edge: Edge functions execute lightweight logic at edge locations. Authentication checks, A/B testing, geolocation-based routing, and API gateway logic run before requests reach the origin server.

Level 3 β€” Edge Applications: Full application logic runs at the edge. This includes server-side rendering, API endpoints, database queries, and business logic β€” all executing within 50ms of the user.

Level 4 β€” Edge Data: Databases replicate data to edge locations, enabling reads and writes with single-digit millisecond latency from anywhere in the world.

Each level adds complexity but reduces latency. The key architectural decision is choosing the right level for each part of your application.

Edge Functions

Edge functions are the building blocks of edge computing. They are lightweight, stateless functions that run at CDN edge locations. Unlike traditional serverless functions that run in a single region, edge functions run in hundreds of locations simultaneously. When a user makes a request, it is routed to the nearest edge location where the function executes.

Edge functions run in constrained environments. They typically use the Web Workers API (Web Standards) rather than Node.js APIs. This means no filesystem access, no native modules, and limited execution time (usually 50ms or less). These constraints are intentional β€” they force developers to build lightweight, fast-executing code that works at the edge.

// Cloudflare Worker example
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const country = request.cf?.country || 'US';
 
    // Geolocation-based routing at the edge
    if (country === 'CN') {
      return Response.redirect('https://cn.example.com' + url.pathname);
    }
 
    // Add security headers at the edge
    const response = await fetch(request);
    const newResponse = new Response(response.body, response);
    newResponse.headers.set('X-Edge-Location', request.cf?.colo || 'unknown');
    newResponse.headers.set('X-Request-Country', country);
 
    return newResponse;
  },
};

Edge Databases

Edge databases bring data storage to the edge. Traditional databases live in a single region, requiring every query to travel to that region. Edge databases replicate data to multiple locations, enabling reads with sub-10ms latency from anywhere in the world.

The challenge is consistency. When data is replicated across 300+ edge locations, keeping it consistent becomes difficult. Most edge databases offer eventual consistency for reads with strong consistency for writes. This trade-off is acceptable for most web applications where reads vastly outnumber writes.

Distributed edge architecture diagram

Architecture and Design Patterns

The Edge-First Architecture

An edge-first architecture designs every component to run at the edge by default, only falling back to the origin when necessary. This inverts the traditional cloud-first model where everything runs in one region and the CDN is an afterthought.

User Request
    β”‚
    β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Edge Node   β”‚  ← Nearest to user
β”‚  (300+ cities)β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ Auth check β”‚
β”‚ β€’ Cache hit? β”‚  ← Cache-aside pattern
β”‚ β€’ A/B test   β”‚
β”‚ β€’ Rate limit β”‚
β”‚ β€’ Geo route  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ Cache miss
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Edge Functionβ”‚  ← Full application logic
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ SSR render β”‚
β”‚ β€’ DB query   β”‚  ← Edge database (Turso, Neon)
β”‚ β€’ API calls  β”‚
β”‚ β€’ Transform  β”‚
β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
       β”‚ Complex operations
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Origin Serverβ”‚  ← Only for heavy compute
β”‚ (Single region)β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ β€’ ML inferenceβ”‚
β”‚ β€’ Batch jobs β”‚
β”‚ β€’ File writesβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The Edge Proxy Pattern

Edge functions excel as proxies. They can transform requests and responses at the edge before they reach the origin:

// Edge proxy with request transformation
export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
 
    // Rewrite API version at the edge
    url.pathname = url.pathname.replace('/v2/', '/v1/');
 
    // Add authentication headers
    const headers = new Headers(request.headers);
    headers.set('Authorization', `Bearer ${env.API_KEY}`);
 
    // Route to appropriate origin based on path
    let origin: string;
    if (url.pathname.startsWith('/api/users')) {
      origin = env.USER_SERVICE_URL;
    } else if (url.pathname.startsWith('/api/products')) {
      origin = env.PRODUCT_SERVICE_URL;
    } else {
      origin = env.DEFAULT_ORIGIN;
    }
 
    // Forward to origin with cache key
    const cacheKey = new Request(url.toString(), {
      method: request.method,
      headers,
    });
 
    const cache = caches.default;
    let response = await cache.match(cacheKey);
 
    if (!response) {
      response = await fetch(new URL(url.pathname, origin), {
        method: request.method,
        headers,
      });
 
      // Cache at edge for 60 seconds
      const cachedResponse = new Response(response.body, response);
      cachedResponse.headers.set('Cache-Control', 's-maxage=60');
      await cache.put(cacheKey, cachedResponse.clone());
 
      return cachedResponse;
    }
 
    return response;
  },
};

The Island Architecture Pattern

For content-heavy sites, the island architecture hydrates interactive components individually while keeping the rest of the page as static HTML rendered at the edge:

// Edge function renders the shell with islands
export default {
  async fetch(request: Request): Promise<Response> {
    const pageData = await fetchPageData(request.url);
 
    const html = `
      <!DOCTYPE html>
      <html>
        <head>
          <link rel="stylesheet" href="/styles/global.css">
        </head>
        <body>
          <!-- Static content rendered at edge -->
          <header>${renderHeader(pageData)}</header>
          <main>
            <article>${pageData.article.content}</article>
 
            <!-- Interactive island: hydrated client-side -->
            <div id="comments-island" data-article-id="${pageData.article.id}">
              <div class="loading">Loading comments...</div>
            </div>
          </main>
          <footer>${renderFooter(pageData)}</footer>
 
          <!-- Only hydrate interactive islands -->
          <script type="module" src="/islands/comments.js"></script>
        </body>
      </html>
    `;
 
    return new Response(html, {
      headers: { 'Content-Type': 'text/html' },
    });
  },
};

The Edge Data Fetching Pattern

Combine edge functions with edge databases for end-to-end low latency:

import { createClient } from '@libsql/client';
 
// Turso edge database client
const db = createClient({
  url: 'libsql://your-db.turso.io',
  authToken: env.TURSO_AUTH_TOKEN,
});
 
export default {
  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const slug = url.pathname.split('/').pop();
 
    // Query edge database β€” latency < 5ms from nearest replica
    const result = await db.execute({
      sql: 'SELECT * FROM articles WHERE slug = ?',
      args: [slug],
    });
 
    if (result.rows.length === 0) {
      return new Response('Not Found', { status: 404 });
    }
 
    const article = result.rows[0];
    return Response.json(article);
  },
};

Step-by-Step Implementation

Let's build a complete edge application using Cloudflare Workers and Hono, a lightweight edge-compatible web framework.

Setting Up the Edge Application

# Create a new Cloudflare Workers project
npm create cloudflare@latest edge-app -- --type=hello-world
cd edge-app
 
# Install Hono framework
npm install hono
 
# Install edge-compatible database client
npm install @libsql/client

Building the Edge Application with Hono

import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { cache } from 'hono/cache';
import { createClient } from '@libsql/client';
 
type Bindings = {
  TURSO_URL: string;
  TURSO_AUTH_TOKEN: string;
  KV: KVNamespace;
};
 
const app = new Hono<{ Bindings: Bindings }>();
 
// Middleware: CORS
app.use('/api/*', cors());
 
// Middleware: Edge caching
app.use(
  '/api/products/*',
  cache({ cacheName: 'products-cache', cacheControl: 'max-age=60' })
);
 
// Route: Get product by ID with edge caching
app.get('/api/products/:id', async (c) => {
  const id = c.req.param('id');
 
  // Check KV cache first (fastest)
  const cached = await c.env.KV.get(`product:${id}`, 'json');
  if (cached) {
    return c.json(cached);
  }
 
  // Query edge database
  const db = createClient({
    url: c.env.TURSO_URL,
    authToken: c.env.TURSO_AUTH_TOKEN,
  });
 
  const result = await db.execute({
    sql: 'SELECT * FROM products WHERE id = ?',
    args: [id],
  });
 
  if (result.rows.length === 0) {
    return c.json({ error: 'Product not found' }, 404);
  }
 
  const product = result.rows[0];
 
  // Cache in KV for 5 minutes
  await c.env.KV.put(`product:${id}`, JSON.stringify(product), {
    expirationTtl: 300,
  });
 
  return c.json(product);
});
 
// Route: Server-side rendering at the edge
app.get('/products/:id', async (c) => {
  const id = c.req.param('id');
  const db = createClient({
    url: c.env.TURSO_URL,
    authToken: c.env.TURSO_AUTH_TOKEN,
  });
 
  const result = await db.execute({
    sql: 'SELECT * FROM products WHERE id = ?',
    args: [id],
  });
 
  const product = result.rows[0];
 
  return c.html(`
    <!DOCTYPE html>
    <html>
      <head><title>${product.name}</title></head>
      <body>
        <h1>${product.name}</h1>
        <p>${product.description}</p>
        <span class="price">$${product.price}</span>
      </body>
    </html>
  `);
});
 
export default app;

Deploying to the Edge

# Deploy to Cloudflare (300+ edge locations globally)
npx wrangler deploy
 
# Verify deployment
curl https://edge-app.your-subdomain.workers.dev/api/products/1

Adding Authentication at the Edge

import { createMiddleware } from 'hono/factory';
 
const authMiddleware = createMiddleware(async (c, next) => {
  const authHeader = c.req.header('Authorization');
 
  if (!authHeader?.startsWith('Bearer ')) {
    return c.json({ error: 'Unauthorized' }, 401);
  }
 
  const token = authHeader.slice(7);
 
  // Verify JWT at the edge (no round-trip to auth server)
  try {
    const payload = await verifyJWT(token, c.env.JWT_SECRET);
    c.set('user', payload);
    await next();
  } catch {
    return c.json({ error: 'Invalid token' }, 401);
  }
});
 
// Apply to protected routes
app.use('/api/admin/*', authMiddleware);

Edge computing deployment workflow

Real-World Use Cases and Case Studies

Use Case 1: E-Commerce Personalization

An e-commerce platform uses edge functions to personalize product recommendations based on the user's geolocation, browsing history (stored in edge KV), and time of day. The personalization logic runs at the edge in under 10ms, compared to 200ms when routed to a centralized server in Virginia. This 190ms improvement resulted in a 7% increase in conversion rate.

Use Case 2: Multi-Region SaaS

A SaaS platform serving global customers uses edge databases to ensure users in any region get sub-20ms database queries. User data is replicated to the nearest edge location on write, and reads are served from the local replica. This eliminates the "US-centric latency" problem that plagues most SaaS products.

Use Case 3: Real-Time API Gateway

An API gateway running at the edge handles rate limiting, authentication, request transformation, and caching for a microservices architecture. By processing these concerns at the edge, the origin services receive only valid, pre-processed requests, reducing their load by 80% and improving response times by 3x.

Use Case 4: Content Delivery with Dynamic Rendering

A news website uses edge functions to render article pages server-side at the edge. When an article is published, the edge function fetches the content from the edge database and renders the full HTML at the nearest edge location. Users get instant page loads regardless of their geographic location, with Time to First Byte under 20ms.

Best Practices for Production

  1. Design for statelessness: Edge functions are ephemeral β€” they don't persist state between requests. Use edge KV, Durable Objects, or edge databases for any state that needs to persist. Keep function execution under 50ms to fit within edge runtime limits.

  2. Minimize bundle size: Edge functions have strict size limits (usually 1MB compressed). Tree-shake aggressively, avoid large dependencies, and use edge-compatible libraries. A 500KB bundle adds 50ms to cold starts.

  3. Use edge-compatible ORMs: Not all database clients work at the edge. Use edge-compatible clients like @libsql/client for Turso, @neondatabase/serverless for Neon, or Prisma's edge client. Standard pg or mysql2 clients do not work in edge runtimes.

  4. Implement graceful degradation: If an edge database is unavailable, fall back to cached data or serve stale content. Users prefer a slightly outdated page over an error page. Use stale-while-revalidate patterns.

  5. Monitor edge-specific metrics: Track latency per edge location, cache hit rates, and cold start times. Cloudflare Analytics and Vercel Analytics provide edge-specific insights. Set alerts for latency spikes at specific edge locations.

  6. Test across edge locations: Use tools like curl with geographic headers or Cloudflare's cf-ray header to verify behavior from different locations. Test with VPN connections to simulate users in different regions.

  7. Leverage edge caching aggressively: Cache at multiple levels β€” browser cache, edge cache, and origin cache. Use cache tags for granular invalidation. A well-cached edge application can serve 95%+ of requests from cache.

  8. Plan for cold starts: Edge functions have cold starts, though they are typically much shorter than traditional serverless (5-20ms vs 100-500ms). Use provisioned concurrency or keep-alive strategies for latency-sensitive endpoints.

Common Pitfalls and Solutions

PitfallImpactSolution
Using Node.js APIs at the edgeRuntime errors, deployment failuresUse Web Standard APIs (Fetch, Web Crypto, Streams)
Large bundle sizes exceeding limitsDeployment failuresTree-shake, use dynamic imports, avoid heavy libraries
Inconsistent data across edge replicasStale reads, race conditionsUse strong consistency for critical writes, eventual for reads
Ignoring cold start latencyFirst-request slownessKeep bundles small, pre-warm critical functions
No fallback for edge failuresComplete outageImplement origin fallback, stale-while-revalidate
Over-caching dynamic contentStale personalizationUse cache tags, vary headers, short TTLs for dynamic content

Performance Optimization

Edge computing delivers measurable latency improvements. Here's a real-world benchmark comparing edge-first vs cloud-first architectures:

// Benchmark: Edge vs Cloud latency
async function benchmarkEdgeVsCloud() {
  const results = {
    edge: [] as number[],
    cloud: [] as number[],
  };
 
  for (let i = 0; i < 100; i++) {
    // Edge request
    const edgeStart = performance.now();
    await fetch('https://edge-app.workers.dev/api/data');
    results.edge.push(performance.now() - edgeStart);
 
    // Cloud request
    const cloudStart = performance.now();
    await fetch('https://api.example.com/data');
    results.cloud.push(performance.now() - cloudStart);
  }
 
  const avgEdge = results.edge.reduce((a, b) => a + b) / results.edge.length;
  const avgCloud = results.cloud.reduce((a, b) => a + b) / results.cloud.length;
 
  console.log(`Edge avg: ${avgEdge.toFixed(1)}ms`);
  console.log(`Cloud avg: ${avgCloud.toFixed(1)}ms`);
  console.log(`Improvement: ${((1 - avgEdge / avgCloud) * 100).toFixed(1)}%`);
}

Comparison with Alternatives

FeatureEdge FunctionsServerless (Lambda)Traditional ServerCDN Only
Cold Start5-20ms100-500msNoneN/A
Global Latency<50ms50-500ms50-500ms<20ms
Execution Limit50ms typical15 minUnlimitedN/A
State ManagementExternal (KV/DB)ExternalLocal/ExternalNone
Node.js APIsLimitedFullFullNone
Bundle Size Limit1-5MB250MBUnlimitedN/A
CostPer-request, cheapPer-requestFixed + variablePer-request

Advanced Patterns

Edge-Side Rendering with Streaming

import { renderToReadableStream } from 'react-dom/server';
 
export default {
  async fetch(request: Request): Promise<Response> {
    const app = <App url={request.url} />;
 
    // Stream HTML from the edge β€” first byte in <20ms
    const stream = await renderToReadableStream(app, {
      bootstrapScripts: ['/client.js'],
    });
 
    return new Response(stream, {
      headers: { 'Content-Type': 'text/html' },
    });
  },
};

Edge Durable Objects for Stateful Edge Computing

// Durable Object β€” stateful edge computing primitive
export class RateLimiter {
  state: DurableObjectState;
 
  constructor(state: DurableObjectState) {
    this.state = state;
  }
 
  async fetch(request: Request): Promise<Response> {
    const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
    const key = `rate:${ip}`;
 
    const current = (await this.state.storage.get<number>(key)) || 0;
    const limit = 100; // requests per minute
 
    if (current >= limit) {
      return new Response('Rate limited', { status: 429 });
    }
 
    await this.state.storage.put(key, current + 1, {
      expirationTtl: 60,
    });
 
    return new Response('OK');
  }
}

Testing Strategies

import { unstable_dev } from 'wrangler';
 
describe('Edge Worker', () => {
  let worker: any;
 
  beforeAll(async () => {
    worker = await unstable_dev('src/index.ts', {
      experimental: { disableExperimentalWarning: true },
    });
  });
 
  afterAll(async () => {
    await worker.stop();
  });
 
  test('returns product data from edge', async () => {
    const resp = await worker.fetch('/api/products/1');
    expect(resp.status).toBe(200);
    const data = await resp.json();
    expect(data).toHaveProperty('name');
    expect(data).toHaveProperty('price');
  });
 
  test('returns 404 for missing product', async () => {
    const resp = await worker.fetch('/api/products/999999');
    expect(resp.status).toBe(404);
  });
 
  test('response includes edge location header', async () => {
    const resp = await worker.fetch('/api/products/1');
    expect(resp.headers.get('X-Edge-Location')).toBeDefined();
  });
});

Future Outlook

Edge computing is converging with serverless to become the default deployment model for web applications. Cloudflare, Vercel, and Deno are investing billions in edge infrastructure. The WebAssembly component model will enable running any language at the edge, not just JavaScript. Edge AI inference will enable intelligent applications that respond in under 10ms.

The gap between edge and cloud capabilities is narrowing rapidly. Edge databases now support transactions, full-text search, and vector operations. Edge functions now support WebSocket connections, long-running tasks (via Durable Objects), and sophisticated caching strategies. Within two years, the "edge-first" architecture will be the default, not the exception.

Conclusion

Edge computing represents a fundamental architectural shift from centralized to distributed computing. By pushing computation to the edge, we achieve latency reductions of 10-100x, improve data sovereignty compliance, and enable new categories of applications.

Key takeaways:

  1. Edge computing exists on a spectrum β€” choose the right level for each component
  2. Design for statelessness; use edge KV, edge databases, or Durable Objects for state
  3. Keep edge functions lightweight β€” under 1MB bundle, under 50ms execution
  4. Use Web Standard APIs, not Node.js APIs, at the edge
  5. Implement graceful degradation with stale-while-revalidate patterns
  6. Monitor edge-specific metrics like per-location latency and cache hit rates

The edge is not the future β€” it is the present. Applications that leverage edge computing today deliver measurably better user experiences than their cloud-only counterparts. Start with edge caching and authentication, then progressively move application logic to the edge as your architecture matures.