Skip to main content

Next.js Edge Functions

In today's fast-paced web environment, delivering content quickly to users around the globe is crucial for any modern application. Next.js Edge Functions provide a powerful solution by running your code at the edge of the network, closer to your users for optimal performance.

What are Edge Functions?

Edge Functions are JavaScript functions that run at the edge of the network, on servers geographically distributed across the world. Unlike traditional server functions that operate in a central location, Edge Functions execute in a location closest to the user making the request, resulting in significantly reduced latency.

In Next.js, Edge Functions are built on top of standard Web APIs, making them lightweight and fast to execute. They're perfect for tasks like:

  • Authentication and authorization
  • Personalization based on user location or data
  • A/B testing
  • Bot protection
  • Response transformation
  • Edge caching and revalidation

Why Use Edge Functions?

Before diving into the implementation, let's understand the key benefits of Edge Functions:

  1. Lower Latency: By running code closer to users, responses are much faster
  2. Global Distribution: Automatic deployment across global edge locations
  3. Cost Efficiency: Edge Functions typically consume fewer resources
  4. Improved User Experience: Faster responses lead to better UX metrics
  5. Cold Start Elimination: Edge Functions have minimal or no cold starts

Creating Your First Edge Function

Let's start by implementing a simple Edge Function in Next.js.

Basic Edge API Route

Create a new file at pages/api/edge-hello.js:

js
export const config = {
runtime: 'edge'
};

export default function handler(req) {
return new Response(
JSON.stringify({ message: 'Hello from the Edge!' }),
{
status: 200,
headers: {
'Content-Type': 'application/json'
}
}
);
}

This simple function returns a JSON response with a greeting message. The important part is the runtime: 'edge' configuration that tells Next.js to deploy this as an Edge Function.

Testing Your Edge Function

When you run your Next.js application and visit /api/edge-hello, you'll see:

json
{
"message": "Hello from the Edge!"
}

Understanding Edge Function Limitations

Edge Functions have some limitations compared to traditional Node.js environments:

  1. Limited Node.js APIs: Only subset of Node.js APIs are available
  2. Limited Size: Edge Functions have code size restrictions (often 1-4MB)
  3. Stateless Execution: No persistent connections or file system access
  4. Execution Time Limits: Usually limited to seconds rather than minutes

Accessing Request Data

Edge Functions can access request data like query parameters, headers, and cookies. Let's create an example that uses location information:

js
export const config = {
runtime: 'edge'
};

export default function handler(req) {
// Access the user's country from the request headers (set by the edge network)
const country = req.headers.get('x-vercel-ip-country') || 'unknown';

return new Response(
JSON.stringify({
message: `Hello from ${country}!`,
timestamp: new Date().toISOString()
}),
{
status: 200,
headers: {
'Content-Type': 'application/json'
}
}
);
}

This function uses geolocation data available at the edge to personalize the response.

Edge Middleware vs. Edge Functions

Next.js offers two edge-based execution models:

  1. Edge Middleware: Runs before a request is completed, can modify responses or redirect
  2. Edge Functions: Complete request handlers that generate responses at the edge

Here's a comparison:

FeatureEdge MiddlewareEdge Functions
Execution timingBefore cache, early in request lifecycleHandles full requests
Use caseAuth, bot protection, rewriting/redirectingAPI endpoints, dynamic content
Access to responseLimited to modifyingCreates response
File location/middleware.js/pages/api/*.js with edge runtime

Practical Example: A/B Testing with Edge Functions

Let's implement a practical example of A/B testing using Edge Functions:

js
export const config = {
runtime: 'edge'
};

export default function handler(req) {
// Get or set a testing group cookie
let testGroup = req.cookies.get('ab-test-group');
const headers = new Headers();

if (!testGroup) {
// Assign user to either group A or B randomly
testGroup = Math.random() < 0.5 ? 'A' : 'B';
headers.append('Set-Cookie', `ab-test-group=${testGroup}; path=/; max-age=86400`);
}

// Return different content based on test group
const content = testGroup === 'A'
? { version: 'A', feature: 'Original layout', color: 'blue' }
: { version: 'B', feature: 'New experimental layout', color: 'green' };

headers.append('Content-Type', 'application/json');

return new Response(
JSON.stringify({
group: testGroup,
content,
timestamp: new Date().toISOString()
}),
{
status: 200,
headers
}
);
}

This Edge Function assigns users to different test groups and returns different content accordingly, enabling effective A/B testing at the edge.

Edge Functions with Server Components

Next.js 13+ introduces Server Components that work seamlessly with Edge Functions. You can create an edge-rendered page by adding a special runtime directive:

jsx
export const runtime = 'edge';

export default async function MyEdgePage() {
const data = await fetch('https://api.example.com/data').then(res => res.json());

return (
<div>
<h1>Edge-rendered Page</h1>
<p>This page is rendered at the edge!</p>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}

This React Server Component will be rendered at the edge, closer to your users.

Geolocation-based Content with Edge Functions

One powerful use case for Edge Functions is serving location-specific content:

jsx
export const runtime = 'edge';

export default async function GeoPage({ params, searchParams }) {
const response = await fetch('https://api.ipgeolocation.io/ipgeo?apiKey=YOUR_API_KEY');
const geo = await response.json();

const countrySpecificOffers = {
US: ['Free shipping on orders over $50', '20% off summer collection'],
UK: ['Free UK delivery', 'Bank holiday special: 15% off'],
// Add more countries as needed
};

const offers = countrySpecificOffers[geo.country_code2] || ['International shipping available', '10% off your first order'];

return (
<div>
<h1>Welcome, visitor from {geo.country_name}!</h1>
<h2>Special Offers For Your Region:</h2>
<ul>
{offers.map((offer, index) => (
<li key={index}>{offer}</li>
))}
</ul>
</div>
);
}

This example fetches geolocation data and serves custom content based on the user's country, all at the edge for optimal performance.

Edge Functions for Authentication

Authentication is another excellent use case for Edge Functions. Here's a simplified example:

js
export const config = {
runtime: 'edge'
};

export default async function handler(req) {
// Get the authorization header
const authHeader = req.headers.get('authorization');

if (!authHeader || !authHeader.startsWith('Bearer ')) {
return new Response(
JSON.stringify({ error: 'Missing or invalid authorization header' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' }
}
);
}

const token = authHeader.split(' ')[1];

try {
// Verify the token (simplified example)
const isValid = await verifyToken(token);

if (!isValid) {
return new Response(
JSON.stringify({ error: 'Invalid token' }),
{
status: 401,
headers: { 'Content-Type': 'application/json' }
}
);
}

// Token is valid, return protected data
return new Response(
JSON.stringify({
message: 'Authentication successful',
data: 'This is protected data from the edge!'
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' }
}
);
} catch (error) {
return new Response(
JSON.stringify({ error: 'Authentication error' }),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
);
}
}

// Simplified token verification function
async function verifyToken(token) {
// In a real app, you would verify against a JWT or check with an auth service
return token === 'valid-token';
}

Deploying Edge Functions

Edge Functions are automatically deployed when you deploy your Next.js application to platforms that support them, like Vercel. No special configuration is needed beyond marking your functions with the edge runtime.

Best Practices for Edge Functions

  1. Keep them small: Edge Functions should be lightweight
  2. Minimize dependencies: Each dependency adds to the bundle size
  3. Use appropriate caching: Implement caching strategies at the edge
  4. Handle errors gracefully: Provide fallbacks when edge execution fails
  5. Monitor performance: Track metrics to ensure optimal execution

Summary

Next.js Edge Functions provide a powerful way to deliver dynamic content with minimal latency by running code close to your users. They're ideal for authentication, personalization, A/B testing, and other use cases where speed and global distribution matter.

Key points to remember:

  • Edge Functions run closer to users for faster response times
  • They're configured with runtime: 'edge' in API routes or export const runtime = 'edge' in pages
  • They use standard Web APIs instead of Node.js APIs
  • They're ideal for personalization, auth, and other latency-sensitive tasks

Additional Resources

Exercises

  1. Create an Edge Function that returns different content based on the time of day in the user's timezone.
  2. Implement a currency converter Edge Function that automatically shows prices in the user's local currency.
  3. Build a multi-language Edge Function that serves content in different languages based on the Accept-Language header.
  4. Create an Edge Function that implements rate limiting to prevent API abuse.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)