Skip to content

Commit b3c2e25

Browse files
authored
Merge pull request #2250 from aziontech/EDU-6638-migrating-handler-patterns
Add migration guide for handler patterns in Functions
2 parents 67b45d6 + 70b6cf5 commit b3c2e25

4 files changed

Lines changed: 587 additions & 0 deletions

File tree

Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
---
2+
title: Migrating handler patterns in Functions
3+
description: >-
4+
Learn how to migrate your Azion Functions from the legacy Service Worker
5+
pattern to the recommended ES Modules pattern, including fetch and firewall
6+
handlers.
7+
meta_tags: 'functions, handler patterns, ES modules, service worker, migration, edge functions'
8+
namespace: documentation_guides_migrate_handler_patterns
9+
permalink: /documentation/products/guides/functions/migrate-handler-patterns/
10+
---
11+
12+
Azion Functions supports two handler patterns: **ES Modules** (recommended) and **Service Worker** (legacy). This guide explains the differences between them and how to migrate your existing functions to the ES Modules pattern.
13+
14+
## Supported patterns
15+
16+
### ES Modules (recommended)
17+
18+
The ES Modules pattern is the recommended way to structure your functions on Azion. It provides a modern, clean syntax with native support in production and better performance.
19+
20+
```javascript
21+
export default {
22+
fetch: (request, env, ctx) => {
23+
return new Response('Hello World');
24+
},
25+
firewall: (request, env, ctx) => {
26+
// Firewall logic
27+
ctx.deny();
28+
}
29+
};
30+
```
31+
32+
### Service Worker (legacy)
33+
34+
The Service Worker pattern is maintained for backward compatibility. If you're using this pattern, Azion recommends migrating to ES Modules.
35+
36+
```javascript
37+
addEventListener('fetch', (event) => {
38+
event.respondWith(handleRequest(event.request));
39+
});
40+
41+
addEventListener('firewall', (event) => {
42+
// Firewall logic
43+
event.deny();
44+
});
45+
46+
async function handleRequest(request) {
47+
return new Response('Hello World');
48+
}
49+
```
50+
51+
---
52+
53+
## Handler parameters
54+
55+
### `fetch(request, env, ctx)`
56+
57+
| Parameter | Type | Description |
58+
|---|---|---|
59+
| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | The incoming HTTP request object |
60+
| `env` | Object | Environment variables and bindings |
61+
| `ctx` | Object | Execution context. Use `ctx.waitUntil(promise)` to extend the function's lifetime for async tasks |
62+
63+
### `firewall(request, env, ctx)` — ES Modules
64+
65+
| Parameter | Type | Description |
66+
|---|---|---|
67+
| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | The incoming HTTP request object |
68+
| `env` | Object | Environment variables and bindings |
69+
| `ctx` | Object | Execution context. Call `ctx.deny()` to block the request immediately. If `ctx.deny()` is not called, the request continues to the `fetch` handler |
70+
71+
### Firewall event — Service Worker
72+
73+
| Property | Description |
74+
|---|---|
75+
| `event.request` | Access to the Request object |
76+
| `event.deny()` | Blocks the request immediately. If not called, the request continues to the `fetch` handler |
77+
78+
---
79+
80+
## Migrating from Service Worker to ES Modules
81+
82+
### Basic fetch handler
83+
84+
**Before (Service Worker):**
85+
86+
```javascript
87+
addEventListener('fetch', (event) => {
88+
event.respondWith(handleRequest(event.request));
89+
});
90+
91+
async function handleRequest(request) {
92+
const url = new URL(request.url);
93+
94+
if (url.pathname === '/api/hello') {
95+
return new Response(JSON.stringify({ message: 'Hello World' }), {
96+
headers: { 'Content-Type': 'application/json' }
97+
});
98+
}
99+
100+
return new Response('Not Found', { status: 404 });
101+
}
102+
```
103+
104+
**After (ES Modules):**
105+
106+
```javascript
107+
export default {
108+
fetch: async (request, env, ctx) => {
109+
const url = new URL(request.url);
110+
111+
if (url.pathname === '/api/hello') {
112+
return new Response(JSON.stringify({ message: 'Hello World' }), {
113+
headers: { 'Content-Type': 'application/json' }
114+
});
115+
}
116+
117+
return new Response('Not Found', { status: 404 });
118+
}
119+
};
120+
```
121+
122+
### Firewall handler
123+
124+
**Before (Service Worker):**
125+
126+
```javascript
127+
addEventListener('fetch', (event) => {
128+
event.respondWith(handleRequest(event.request));
129+
});
130+
131+
addEventListener('firewall', (event) => {
132+
const clientIP = event.request.headers.get('X-Forwarded-For');
133+
const userAgent = event.request.headers.get('User-Agent');
134+
135+
// Block bot requests
136+
if (userAgent && userAgent.includes('bot')) {
137+
event.deny();
138+
return;
139+
}
140+
141+
// Block specific IPs
142+
if (clientIP === '192.168.1.100') {
143+
event.deny();
144+
return;
145+
}
146+
147+
// Allow request to continue to fetch handler
148+
});
149+
150+
async function handleRequest(request) {
151+
return new Response('Hello World');
152+
}
153+
```
154+
155+
**After (ES Modules):**
156+
157+
```javascript
158+
export default {
159+
fetch: async (request, env, ctx) => {
160+
return new Response('Access granted');
161+
},
162+
163+
firewall: async (request, env, ctx) => {
164+
const clientIP = request.headers.get('X-Forwarded-For');
165+
const userAgent = request.headers.get('User-Agent');
166+
167+
// Block bot requests
168+
if (userAgent && userAgent.includes('bot')) {
169+
ctx.deny();
170+
return;
171+
}
172+
173+
// Block specific IPs
174+
if (clientIP === '192.168.1.100') {
175+
ctx.deny();
176+
return;
177+
}
178+
179+
// Allow request to continue to fetch handler
180+
return;
181+
}
182+
};
183+
```
184+
185+
### Using `waitUntil` for async tasks
186+
187+
```javascript
188+
export default {
189+
fetch: async (request, env, ctx) => {
190+
// Use waitUntil for async tasks that should not block the response
191+
ctx.waitUntil(logRequest(request));
192+
193+
return new Response('Hello World');
194+
}
195+
};
196+
197+
async function logRequest(request) {
198+
console.log(`Request to: ${request.url}`);
199+
}
200+
```
201+
202+
### Advanced firewall with path-based rules
203+
204+
```javascript
205+
export default {
206+
fetch: async (request, env, ctx) => {
207+
return new Response('Access granted');
208+
},
209+
210+
firewall: async (request, env, ctx) => {
211+
const url = new URL(request.url);
212+
const userAgent = request.headers.get('User-Agent');
213+
const clientIP = request.headers.get('X-Forwarded-For');
214+
215+
// Block bot requests
216+
if (userAgent && userAgent.includes('bot')) {
217+
ctx.deny();
218+
return;
219+
}
220+
221+
// Restrict access to admin paths by IP range
222+
if (url.pathname.startsWith('/admin')) {
223+
if (!clientIP || !clientIP.startsWith('192.168.')) {
224+
ctx.deny();
225+
return;
226+
}
227+
}
228+
229+
// Allow request to continue to fetch handler
230+
return;
231+
}
232+
};
233+
```
234+
235+
---
236+
237+
## Unsupported patterns
238+
239+
The following patterns are **not** supported by Azion Functions. If your code uses any of them, migrate to the ES Modules pattern.
240+
241+
```javascript
242+
// ❌ Direct function export
243+
export default function(request) {
244+
return new Response('Hello');
245+
}
246+
247+
// ❌ Named exports
248+
export function fetch(request) {
249+
return new Response('Hello');
250+
}
251+
252+
// ❌ No export
253+
function handleRequest(request) {
254+
return new Response('Hello');
255+
}
256+
```
257+
258+
---
259+
260+
## Troubleshooting
261+
262+
### "Unsupported handler pattern detected"
263+
264+
This error appears when your code doesn't follow any of the supported patterns. To resolve it, migrate to the ES Modules pattern:
265+
266+
```javascript
267+
export default {
268+
fetch: async (request, env, ctx) => {
269+
return new Response('Hello World');
270+
}
271+
};
272+
```
273+
274+
Alternatively, use the Service Worker pattern as a temporary measure:
275+
276+
```javascript
277+
addEventListener('fetch', (event) => {
278+
event.respondWith(handleRequest(event.request));
279+
});
280+
281+
async function handleRequest(request) {
282+
return new Response('Hello World');
283+
}
284+
```
285+
286+
---
287+
288+
## Related resources
289+
290+
- [Functions first steps](/en/documentation/products/guides/edge-functions/first-steps/)
291+
- [Functions overview](/en/documentation/products/build/applications/functions/)
292+
- [Azion Runtime API reference](/en/documentation/runtime/overview/)
293+
- [Functions with Firewall](/en/documentation/products/guides/edge-functions/firewall/)

src/content/docs/en/pages/guides/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ permalink: /documentation/products/guides/
6060
- [How to build a RESTful API with Functions and SQL Database](/en/documentation/products/guides/build/restful-tasks-api-functions/)
6161
- [How to handle Stripe webhooks with Functions](/en/documentation/products/guides/build/stripe-webhooks-functions/)
6262
- [How to run serverless functions on Azion Console](/en/documentation/products/guides/serverless-functions/)
63+
- [How to migrate handler patterns in Functions](/en/documentation/products/guides/functions/migrate-handler-patterns/)
6364

6465
### Azion Templates
6566

0 commit comments

Comments
 (0)