Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@

```javascript
// functions/_middleware.js (global) or functions/users/_middleware.js (scoped to /users/*)

// Single
export async function onRequest(context) {
try {
return await context.next();
} catch (err) {
return new Response(`${err.message}\n${err.stack}`, { status: 500 });
}
try { return await context.next(); }
catch (err) { return new Response(`${err.message}\n${err.stack}`, { status: 500 }); }
}

// Chained (runs in array order)
Expand All @@ -24,14 +19,11 @@ export const onRequest = [errorHandling, authentication, logging];
async function authMiddleware(context: EventContext<Env>) {
const token = context.request.headers.get('authorization')?.replace('Bearer ', '');
if (!token) return new Response('Unauthorized', { status: 401 });

const session = await context.env.KV.get(`session:${token}`);
if (!session) return new Response('Invalid token', { status: 401 });

context.data.user = JSON.parse(session);
return context.next();
}

export const onRequest = [authMiddleware];
```

Expand All @@ -44,11 +36,9 @@ const corsHeaders = {
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};

export async function onRequestOptions(context) {
export async function onRequestOptions() {
return new Response(null, { headers: corsHeaders });
}

export async function onRequest(context) {
const response = await context.next();
Object.entries(corsHeaders).forEach(([k, v]) => response.headers.set(k, v));
Expand All @@ -60,14 +50,11 @@ export async function onRequest(context) {

```typescript
interface Env { RATE_LIMIT: KVNamespace; }

async function rateLimitMiddleware(context: EventContext<Env>) {
const clientIP = context.request.headers.get('CF-Connecting-IP') || 'unknown';
const key = `ratelimit:${clientIP}`;
const count = parseInt(await context.env.RATE_LIMIT.get(key) || '0');

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

await context.env.RATE_LIMIT.put(key, (count + 1).toString(), { expirationTtl: 3600 });
return context.next();
}
Expand All @@ -78,17 +65,12 @@ async function rateLimitMiddleware(context: EventContext<Env>) {
```typescript
export async function onRequestPost(context) {
const contentType = context.request.headers.get('content-type') || '';

if (contentType.includes('application/json')) {
const data = await context.request.json();
return Response.json({ received: data });
return Response.json({ received: await context.request.json() });
}

if (contentType.includes('application/x-www-form-urlencoded')) {
const formData = await context.request.formData();
return Response.json({ received: Object.fromEntries(formData) });
return Response.json({ received: Object.fromEntries(await context.request.formData()) });
}

if (contentType.includes('multipart/form-data')) {
const formData = await context.request.formData();
const file = formData.get('file') as File;
Expand All @@ -97,7 +79,6 @@ export async function onRequestPost(context) {
return Response.json({ uploaded: file.name });
}
}

return new Response('Unsupported content type', { status: 400 });
}
```
Expand All @@ -108,14 +89,12 @@ export async function onRequestPost(context) {
export async function onRequest(context) {
const cache = caches.default;
const cacheKey = new Request(context.request.url, context.request);

let response = await cache.match(cacheKey);
if (!response) {
response = new Response('Hello World');
response.headers.set('Cache-Control', 'public, max-age=3600');
context.waitUntil(cache.put(cacheKey, response.clone()));
}

return response;
}
```
Expand All @@ -125,41 +104,24 @@ export async function onRequest(context) {
```typescript
export async function onRequest(context) {
const url = new URL(context.request.url);

if (url.pathname === '/old-page') {
return Response.redirect(`${url.origin}/new-page`, 301);
}

if (url.protocol === 'http:') {
url.protocol = 'https:';
return Response.redirect(url.toString(), 301);
}

if (url.pathname === '/old-page') return Response.redirect(`${url.origin}/new-page`, 301);
if (url.protocol === 'http:') { url.protocol = 'https:'; return Response.redirect(url.toString(), 301); }
return context.next();
}
```

## Advanced Mode (`_worker.js`)

Replace `/functions` with `_worker.js` for full routing control (complex Workers, framework-generated output: Next.js, SvelteKit).
Replace `/functions` with `_worker.js` for full routing control (complex Workers, framework output: Next.js, SvelteKit).

```typescript
// Module Worker syntax required; /functions ignored when _worker.js present
// Manually call env.ASSETS.fetch() for static files; passThroughOnException() unavailable

interface Env {
ASSETS: Fetcher;
KV: KVNamespace;
}

interface Env { ASSETS: Fetcher; KV: KVNamespace; }
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);

if (url.pathname.startsWith('/api/')) {
return new Response('API response');
}

if (url.pathname.startsWith('/api/')) return new Response('API response');
return env.ASSETS.fetch(request);
}
} satisfies ExportedHandler<Env>;
Expand Down
Loading