Skip to content
Open
Show file tree
Hide file tree
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
@@ -0,0 +1,293 @@
---
title: Migrating handler patterns in Functions
description: >-
Learn how to migrate your Azion Functions from the legacy Service Worker
pattern to the recommended ES Modules pattern, including fetch and firewall
handlers.
meta_tags: 'functions, handler patterns, ES modules, service worker, migration, edge functions'
namespace: documentation_guides_migrate_handler_patterns
permalink: /documentation/products/guides/functions/migrate-handler-patterns/
---

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.

## Supported patterns

### ES Modules (recommended)

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.

```javascript
export default {
fetch: (request, env, ctx) => {
return new Response('Hello World');
},
firewall: (request, env, ctx) => {
// Firewall logic
ctx.deny();
}
};
```

### Service Worker (legacy)

The Service Worker pattern is maintained for backward compatibility. If you're using this pattern, Azion recommends migrating to ES Modules.

```javascript
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});

addEventListener('firewall', (event) => {
// Firewall logic
event.deny();
});

async function handleRequest(request) {
return new Response('Hello World');
}
```

---

## Handler parameters

### `fetch(request, env, ctx)`

| Parameter | Type | Description |
|---|---|---|
| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | The incoming HTTP request object |
| `env` | Object | Environment variables and bindings |
| `ctx` | Object | Execution context. Use `ctx.waitUntil(promise)` to extend the function's lifetime for async tasks |

### `firewall(request, env, ctx)` — ES Modules

| Parameter | Type | Description |
|---|---|---|
| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | The incoming HTTP request object |
| `env` | Object | Environment variables and bindings |
| `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 |

### Firewall event — Service Worker

| Property | Description |
|---|---|
| `event.request` | Access to the Request object |
| `event.deny()` | Blocks the request immediately. If not called, the request continues to the `fetch` handler |

---

## Migrating from Service Worker to ES Modules

### Basic fetch handler

**Before (Service Worker):**

```javascript
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
const url = new URL(request.url);

if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({ message: 'Hello World' }), {
headers: { 'Content-Type': 'application/json' }
});
}

return new Response('Not Found', { status: 404 });
}
```

**After (ES Modules):**

```javascript
export default {
fetch: async (request, env, ctx) => {
const url = new URL(request.url);

if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({ message: 'Hello World' }), {
headers: { 'Content-Type': 'application/json' }
});
}

return new Response('Not Found', { status: 404 });
}
};
```

### Firewall handler

**Before (Service Worker):**

```javascript
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});

addEventListener('firewall', (event) => {
const clientIP = event.request.headers.get('X-Forwarded-For');
const userAgent = event.request.headers.get('User-Agent');

// Block bot requests
if (userAgent && userAgent.includes('bot')) {
event.deny();
return;
}

// Block specific IPs
if (clientIP === '192.168.1.100') {
event.deny();
return;
}

// Allow request to continue to fetch handler
});

async function handleRequest(request) {
return new Response('Hello World');
}
```

**After (ES Modules):**

```javascript
export default {
fetch: async (request, env, ctx) => {
return new Response('Access granted');
},

firewall: async (request, env, ctx) => {
const clientIP = request.headers.get('X-Forwarded-For');
const userAgent = request.headers.get('User-Agent');

// Block bot requests
if (userAgent && userAgent.includes('bot')) {
ctx.deny();
return;
}

// Block specific IPs
if (clientIP === '192.168.1.100') {
ctx.deny();
return;
}

// Allow request to continue to fetch handler
return;
}
};
```

### Using `waitUntil` for async tasks

```javascript
export default {
fetch: async (request, env, ctx) => {
// Use waitUntil for async tasks that should not block the response
ctx.waitUntil(logRequest(request));

return new Response('Hello World');
}
};

async function logRequest(request) {
console.log(`Request to: ${request.url}`);
}
```

### Advanced firewall with path-based rules

```javascript
export default {
fetch: async (request, env, ctx) => {
return new Response('Access granted');
},

firewall: async (request, env, ctx) => {
const url = new URL(request.url);
const userAgent = request.headers.get('User-Agent');
const clientIP = request.headers.get('X-Forwarded-For');

// Block bot requests
if (userAgent && userAgent.includes('bot')) {
ctx.deny();
return;
}

// Restrict access to admin paths by IP range
if (url.pathname.startsWith('/admin')) {
if (!clientIP || !clientIP.startsWith('192.168.')) {
ctx.deny();
return;
}
}

// Allow request to continue to fetch handler
return;
}
};
```

---

## Unsupported patterns

The following patterns are **not** supported by Azion Functions. If your code uses any of them, migrate to the ES Modules pattern.

```javascript
// ❌ Direct function export
export default function(request) {
return new Response('Hello');
}

// ❌ Named exports
export function fetch(request) {
return new Response('Hello');
}

// ❌ No export
function handleRequest(request) {
return new Response('Hello');
}
```

---

## Troubleshooting

### "Unsupported handler pattern detected"

This error appears when your code doesn't follow any of the supported patterns. To resolve it, migrate to the ES Modules pattern:

```javascript
export default {
fetch: async (request, env, ctx) => {
return new Response('Hello World');
}
};
```

Alternatively, use the Service Worker pattern as a temporary measure:

```javascript
addEventListener('fetch', (event) => {
event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
return new Response('Hello World');
}
```

---

## Related resources

- [Functions first steps](/en/documentation/products/guides/edge-functions/first-steps/)
- [Functions overview](/en/documentation/products/build/applications/functions/)
- [Azion Runtime API reference](/en/documentation/runtime/overview/)
- [Functions with Firewall](/en/documentation/products/guides/edge-functions/firewall/)
1 change: 1 addition & 0 deletions src/content/docs/en/pages/guides/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ permalink: /documentation/products/guides/
- [How to build a RESTful API with Functions and SQL Database](/en/documentation/products/guides/build/restful-tasks-api-functions/)
- [How to handle Stripe webhooks with Functions](/en/documentation/products/guides/build/stripe-webhooks-functions/)
- [How to run serverless functions on Azion Console](/en/documentation/products/guides/serverless-functions/)
- [How to migrate handler patterns in Functions](/en/documentation/products/guides/functions/migrate-handler-patterns/)

### Azion Templates

Expand Down
Loading