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 → UserPersonalization 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=TestRemote 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/dataMigration 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 logicStep 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!