skills-ws
Version:
87 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.
74 lines (57 loc) • 2.42 kB
Markdown
## Contents
- 5. Edge Functions & Middleware
- Edge API routes
## 5. Edge Functions & Middleware
```tsx
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';
import { geolocation } from '@vercel/functions'; // npm i @vercel/functions
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Geo-routing. NOTE: `request.geo`/`request.ip` were REMOVED from core
// Next.js in v15 — reading them now is undefined. Geo data is provider-supplied:
// - Vercel: geolocation(request).country (from @vercel/functions)
// - Cloudflare: request.headers.get('cf-ipcountry')
// - Other CDNs: a header like 'x-vercel-ip-country' / 'x-geo-country'
// Self-hosted (node/standalone) gets NO geo unless your proxy injects a header.
const country = geolocation(request).country ?? 'US';
if (pathname === '/' && country === 'DE' && !request.cookies.has('geo-override')) {
return NextResponse.redirect(new URL('/de', request.url));
}
// A/B testing at the edge — no client flicker
if (pathname === '/pricing') {
const bucket = request.cookies.get('ab-pricing')?.value
?? (Math.random() < 0.5 ? 'control' : 'variant');
const res = NextResponse.rewrite(new URL(`/pricing/${bucket}`, request.url));
if (!request.cookies.has('ab-pricing')) {
res.cookies.set('ab-pricing', bucket, { maxAge: 60 * 60 * 24 * 30, httpOnly: true });
}
return res;
}
// Bot detection — serve pre-rendered for crawlers
const ua = request.headers.get('user-agent') ?? '';
if (/bot|crawler|spider|googlebot/i.test(ua) && pathname.startsWith('/app')) {
return NextResponse.rewrite(new URL(`/seo${pathname}`, request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|api/).*)'],
};
```
### Edge API routes
```tsx
// app/api/edge-search/route.ts
export const runtime = 'edge';
export async function GET(req: NextRequest) {
const q = req.nextUrl.searchParams.get('q');
if (!q) return NextResponse.json({ results: [] });
const results = await fetch(`https://api.example.com/search?q=${encodeURIComponent(q)}`, {
headers: { Authorization: `Bearer ${process.env.API_KEY}` },
}).then(r => r.json());
return NextResponse.json(results, {
headers: { 'Cache-Control': 's-maxage=60, stale-while-revalidate=300' },
});
}
```
---