Skip to content
All essays
CraftMarch 27, 202413 min

Edge Computing for Frontend Developers

Deploy and run code closer to users for lightning-fast performance.

Ü
Ümit Uz
Mobile & Full Stack Developer

Edge computing brings your code closer to users, reducing latency and improving performance. Here's how frontend developers can leverage edge computing.

What Is Edge Computing?

Edge computing distributes computation across a global network of servers, allowing code to run closer to users.

javascript
// Traditional: Code runs in one region
User → Request → US East Server → Response (200ms latency)

// Edge: Code runs nearest to user
User → Request → Nearest Edge Location → Response (20ms latency)

Benefits for Frontend Development

Reduced Latency

javascript
// Traditional API route
export async function GET() {
  const data = await fetch('https://api.example.com/data');
  return Response.json(await data.json());
}
// Latency: User → Origin → API → Origin → User

// Edge function
export async function GET({ request }) {
  const data = await fetch('https://api.example.com/data');
  return Response.json(await data.json());
}
// Latency: User → Edge → API → Edge → User

Personalization at the Edge

javascript
// Vercel Edge Function
export const config = {
  runtime: 'edge',
};

export default async function handler(request) {
  const country = request.geo?.country;
  const city = request.geo?.city;

  // Serve personalized content
  const content = await getContentByLocation(country, city);

  return Response.json({
    content,
    location: { country, city },
  });
}

A/B Testing

javascript
export default async function handler(request) {
  const userId = getUserId(request);
  const variant = await getVariant(userId);

  // Edge makes decisions instantly
  if (variant === 'A') {
    return Response.json({ version: 'A', features: ['feature1'] });
  } else {
    return Response.json({ version: 'B', features: ['feature1', 'feature2'] });
  }
}

Edge Platforms

Vercel Edge Functions

javascript
// api/hello.js
export const config = {
  runtime: 'edge',
};

export default async function handler(request) {
  const url = new URL(request.url);
  const name = url.searchParams.get('name') || 'World';

  return new Response(`Hello, ${name}!`, {
    headers: {
      'content-type': 'text/plain',
    },
  });
}

Cloudflare Workers

javascript
// worker.js
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    if (url.pathname === '/api/data') {
      const data = await env.DB.prepare(
        'SELECT * FROM users'
      ).all();

      return Response.json(data.results);
    }

    return new Response('Not Found', { status: 404 });
  },
};

Netlify Edge Functions

javascript
// netlify/edge-functions/hello.js
export default async (request, context) => {
  const { geo } = context;

  return new Response(JSON.stringify({
    country: geo.country?.code,
    city: geo.city,
  }), {
    headers: {
      'Content-Type': 'application/json',
    },
  });
};

Use Cases

Dynamic Routing

javascript
// Redirect based on location
export default async function middleware(request) {
  const { country } = request.geo;

  if (country === 'US') {
    return Response.redirect('https://example.com/us');
  } else if (country === 'UK') {
    return Response.redirect('https://example.com/uk');
  }

  return Response.redirect('https://example.com/global');
}

Auth at the Edge

javascript
import { verifyAuth } from '@lib/auth';

export default async function middleware(request) {
  const token = request.headers.get('authorization');

  if (!token) {
    return Response.redirect('/login');
  }

  const user = await verifyAuth(token);

  if (!user) {
    return Response.redirect('/login');
  }

  // Add user to headers for downstream
  const requestHeaders = new Headers(request.headers);
  requestHeaders.set('x-user-id', user.id);

  return Response.next({
    request: {
      headers: requestHeaders,
    },
  });
}

Rate Limiting

javascript
// Using Cloudflare KV
export default {
  async fetch(request, env) {
    const ip = request.headers.get('CF-Connecting-IP');
    const key = `rate-limit:${ip}`;

    const count = await env.KV.get(key);

    if (count && parseInt(count) > 100) {
      return new Response('Rate limit exceeded', { status: 429 });
    }

    await env.KV.put(key, (parseInt(count || '0') + 1).toString(), {
      expirationTtl: 60,
    });

    return Response.next();
  },
};

API Aggregation

javascript
export default async function handler(request) {
  const [users, posts, comments] = await Promise.all([
    fetch('https://api.example.com/users').then(r => r.json()),
    fetch('https://api.example.com/posts').then(r => r.json()),
    fetch('https://api.example.com/comments').then(r => r.json()),
  ]);

  return Response.json({
    users: users.slice(0, 10),
    posts: posts.slice(0, 10),
    comments: comments.slice(0, 10),
  });
}

Caching Strategies

Cache-First

javascript
export default async function handler(request) {
  const cache = caches.default;
  const cacheKey = new Request(request.url, request);

  let response = await cache.match(cacheKey);

  if (!response) {
    // Cache miss - fetch from origin
    response = await fetch(request.url);

    // Cache for 1 hour
    response = new Response(response.body, response);
    response.headers.set('Cache-Control', 'public, max-age=3600');

    await cache.put(cacheKey, response.clone());
  }

  return response;
}

Stale-While-Revalidate

javascript
export default async function handler(request) {
  const cache = caches.default;
  const cacheKey = new Request(request.url, request);

  const cached = await cache.match(cacheKey);

  if (cached) {
    // Return cached immediately
    const context = await ctx.waitUntil(
      fetch(request.url).then(response => {
        return cache.put(cacheKey, response);
      })
    );

    return cached;
  }

  // No cache - fetch and cache
  const response = await fetch(request.url);
  await cache.put(cacheKey, response.clone());

  return response;
}

Personalization

Geo-Based Content

javascript
export default async function handler(request) {
  const { geo } = request;

  const products = await fetch(
    `https://api.example.com/products?country=${geo.country}`
  ).then(r => r.json());

  return Response.json(products);
}

Device Detection

javascript
export default async function handler(request) {
  const userAgent = request.headers.get('user-agent');
  const isMobile = /mobile/i.test(userAgent);

  if (isMobile) {
    return Response.json({ layout: 'mobile' });
  }

  return Response.json({ layout: 'desktop' });
}

Performance Monitoring

Real User Monitoring (RUM)

javascript
export default async function handler(request) {
  const start = Date.now();

  const response = await fetch('https://api.example.com/data');

  const duration = Date.now() - start;

  // Send metrics to analytics
  await fetch('https://analytics.example.com/edge-metrics', {
    method: 'POST',
    body: JSON.stringify({
      path: request.url,
      duration,
      edgeLocation: request.cf?.colo,
    }),
  });

  return response;
}

Best Practices

DOs

  • Keep functions small and focused
  • Use edge for latency-sensitive operations
  • Leverage geolocation for personalization
  • Implement proper caching strategies
  • Monitor performance
  • Handle errors gracefully

DON'Ts

  • Run heavy computations at the edge
  • Make long-running requests
  • Use too many external service calls
  • Ignore cold start times
  • Forget about cache invalidation
  • Overcomplicate logic

Testing Edge Functions

Local Testing

javascript
// Using Vercel CLI
// vercel dev

// Using Cloudflare Wrangler
// wrangler dev

// Test with curl
curl http://localhost:3000/api/hello?name=Test

Remote Testing

bash
# Test from different locations
# US East
curl -H "CF-IPCountry: US" https://your-api.com/api/data

# Europe
curl -H "CF-IPCountry: GB" https://your-api.com/api/data

# Asia
curl -H "CF-IPCountry: JP" https://your-api.com/api/data

Migration Strategy

Step 1: Identify Candidates

javascript
// Good for edge:
- Authentication middleware
- Rate limiting
- A/B testing
- Geo-routing
- API aggregation
- Static content serving

// Not good for edge:
- Heavy data processing
- Long-running operations
- Large file uploads
- Complex business logic

Step 2: Migrate Gradually

javascript
// Start with simple routes
export default async function handler(request) {
  return Response.json({ message: 'Hello from edge!' });
}

// Then add complexity
export default async function handler(request) {
  const data = await fetchData();
  const personalized = personalize(data, request.geo);
  return Response.json(personalized);
}

Performance Tips

Minimize Bundle Size

javascript
// ❌ Large dependencies
import { bigLibrary } from 'big-library';

// ✅ Use native APIs or smaller libraries
export default async function handler(request) {
  const data = await fetch('https://api.example.com/data');
  return Response.json(await data.json());
}

Use Streaming

javascript
export default async function handler(request) {
  const response = await fetch('https://api.example.com/stream');

  return new Response(response.body, {
    headers: {
      'Content-Type': 'text/event-stream',
    },
  });
}

Conclusion

Edge computing enables frontend developers to build faster, more personalized applications. Start with simple use cases like routing and auth, then gradually move more logic to the edge. The key is understanding when to use edge vs origin servers. Your users will notice the difference!

Enjoyed this essay? Share it.

Related essays

Next essay
React Server Components Complete Guide