From 70b6cf5b36ef7f2a76bfdb149cecb7f903b13e23 Mon Sep 17 00:00:00 2001 From: "gabriel.alves" Date: Fri, 10 Jul 2026 11:05:51 -0300 Subject: [PATCH] Add migration guide for handler patterns in Functions --- .../migrate-handler-patterns.mdx | 293 ++++++++++++++++++ src/content/docs/en/pages/guides/index.mdx | 1 + .../migrate-handler-patterns.mdx | 292 +++++++++++++++++ src/content/docs/pt-br/pages/guias/guides.mdx | 1 + 4 files changed, 587 insertions(+) create mode 100644 src/content/docs/en/pages/guides/edge-functions/migrate-handler-patterns.mdx create mode 100644 src/content/docs/pt-br/pages/guias/edge-functions/migrate-handler-patterns.mdx diff --git a/src/content/docs/en/pages/guides/edge-functions/migrate-handler-patterns.mdx b/src/content/docs/en/pages/guides/edge-functions/migrate-handler-patterns.mdx new file mode 100644 index 0000000000..9601e60fe7 --- /dev/null +++ b/src/content/docs/en/pages/guides/edge-functions/migrate-handler-patterns.mdx @@ -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/) diff --git a/src/content/docs/en/pages/guides/index.mdx b/src/content/docs/en/pages/guides/index.mdx index 8b7c360fd5..deef23e816 100644 --- a/src/content/docs/en/pages/guides/index.mdx +++ b/src/content/docs/en/pages/guides/index.mdx @@ -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 diff --git a/src/content/docs/pt-br/pages/guias/edge-functions/migrate-handler-patterns.mdx b/src/content/docs/pt-br/pages/guias/edge-functions/migrate-handler-patterns.mdx new file mode 100644 index 0000000000..5a10203bf3 --- /dev/null +++ b/src/content/docs/pt-br/pages/guias/edge-functions/migrate-handler-patterns.mdx @@ -0,0 +1,292 @@ +--- +title: Migrando padrões de handler em Functions +description: >- + Aprenda a migrar suas Azion Functions do padrão legado Service Worker para o + padrão recomendado ES Modules, incluindo os handlers fetch e firewall. +meta_tags: 'functions, handler patterns, ES modules, service worker, migração, edge functions' +namespace: documentation_guides_migrate_handler_patterns +permalink: /documentacao/produtos/guias/functions/migrar-padroes-de-handler/ +--- + +As Azion Functions suportam dois padrões de handler: **ES Modules** (recomendado) e **Service Worker** (legado). Este guia explica as diferenças entre eles e como migrar suas functions existentes para o padrão ES Modules. + +## Padrões suportados + +### ES Modules (recomendado) + +O padrão ES Modules é a forma recomendada de estruturar suas functions na Azion. Ele oferece uma sintaxe moderna e limpa, com suporte nativo em produção e melhor desempenho. + +```javascript +export default { + fetch: (request, env, ctx) => { + return new Response('Hello World'); + }, + firewall: (request, env, ctx) => { + // Lógica de firewall + ctx.deny(); + } +}; +``` + +### Service Worker (legado) + +O padrão Service Worker é mantido para compatibilidade com código legado. Se você estiver usando esse padrão, a Azion recomenda migrar para ES Modules. + +```javascript +addEventListener('fetch', (event) => { + event.respondWith(handleRequest(event.request)); +}); + +addEventListener('firewall', (event) => { + // Lógica de firewall + event.deny(); +}); + +async function handleRequest(request) { + return new Response('Hello World'); +} +``` + +--- + +## Parâmetros dos handlers + +### `fetch(request, env, ctx)` + +| Parâmetro | Tipo | Descrição | +|---|---|---| +| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | O objeto da requisição HTTP recebida | +| `env` | Object | Variáveis de ambiente e bindings | +| `ctx` | Object | Contexto de execução. Use `ctx.waitUntil(promise)` para estender o tempo de vida da function para tarefas assíncronas | + +### `firewall(request, env, ctx)` — ES Modules + +| Parâmetro | Tipo | Descrição | +|---|---|---| +| `request` | [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) | O objeto da requisição HTTP recebida | +| `env` | Object | Variáveis de ambiente e bindings | +| `ctx` | Object | Contexto de execução. Chame `ctx.deny()` para bloquear a requisição imediatamente. Se `ctx.deny()` não for chamado, a requisição continua para o handler `fetch` | + +### Evento de firewall — Service Worker + +| Propriedade | Descrição | +|---|---| +| `event.request` | Acesso ao objeto Request | +| `event.deny()` | Bloqueia a requisição imediatamente. Se não for chamado, a requisição continua para o handler `fetch` | + +--- + +## Migrando de Service Worker para ES Modules + +### Handler fetch básico + +**Antes (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 }); +} +``` + +**Depois (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 }); + } +}; +``` + +### Handler firewall + +**Antes (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'); + + // Bloquear requisições de bots + if (userAgent && userAgent.includes('bot')) { + event.deny(); + return; + } + + // Bloquear IPs específicos + if (clientIP === '192.168.1.100') { + event.deny(); + return; + } + + // Permitir que a requisição continue para o handler fetch +}); + +async function handleRequest(request) { + return new Response('Hello World'); +} +``` + +**Depois (ES Modules):** + +```javascript +export default { + fetch: async (request, env, ctx) => { + return new Response('Acesso concedido'); + }, + + firewall: async (request, env, ctx) => { + const clientIP = request.headers.get('X-Forwarded-For'); + const userAgent = request.headers.get('User-Agent'); + + // Bloquear requisições de bots + if (userAgent && userAgent.includes('bot')) { + ctx.deny(); + return; + } + + // Bloquear IPs específicos + if (clientIP === '192.168.1.100') { + ctx.deny(); + return; + } + + // Permitir que a requisição continue para o handler fetch + return; + } +}; +``` + +### Usando `waitUntil` para tarefas assíncronas + +```javascript +export default { + fetch: async (request, env, ctx) => { + // Use waitUntil para tarefas assíncronas que não devem bloquear a resposta + ctx.waitUntil(logRequest(request)); + + return new Response('Hello World'); + } +}; + +async function logRequest(request) { + console.log(`Requisição para: ${request.url}`); +} +``` + +### Firewall avançado com regras baseadas em path + +```javascript +export default { + fetch: async (request, env, ctx) => { + return new Response('Acesso concedido'); + }, + + 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'); + + // Bloquear requisições de bots + if (userAgent && userAgent.includes('bot')) { + ctx.deny(); + return; + } + + // Restringir acesso a paths de admin por faixa de IP + if (url.pathname.startsWith('/admin')) { + if (!clientIP || !clientIP.startsWith('192.168.')) { + ctx.deny(); + return; + } + } + + // Permitir que a requisição continue para o handler fetch + return; + } +}; +``` + +--- + +## Padrões não suportados + +Os seguintes padrões **não** são suportados pelas Azion Functions. Se o seu código utilizar algum deles, migre para o padrão ES Modules. + +```javascript +// ❌ Export direto de função +export default function(request) { + return new Response('Hello'); +} + +// ❌ Named exports +export function fetch(request) { + return new Response('Hello'); +} + +// ❌ Sem export +function handleRequest(request) { + return new Response('Hello'); +} +``` + +--- + +## Solução de problemas + +### "Unsupported handler pattern detected" + +Esse erro aparece quando o código não segue nenhum dos padrões suportados. Para resolver, migre para o padrão ES Modules: + +```javascript +export default { + fetch: async (request, env, ctx) => { + return new Response('Hello World'); + } +}; +``` + +Como alternativa temporária, use o padrão Service Worker: + +```javascript +addEventListener('fetch', (event) => { + event.respondWith(handleRequest(event.request)); +}); + +async function handleRequest(request) { + return new Response('Hello World'); +} +``` + +--- + +## Recursos relacionados + +- [Primeiros passos com Functions](/pt-br/documentacao/produtos/guias/edge-functions/primeiros-passos/) +- [Visão geral de Functions](/pt-br/documentacao/produtos/build/applications/functions/) +- [Referência da API do Azion Runtime](/pt-br/documentacao/runtime/visao-geral/) +- [Functions com Firewall](/pt-br/documentacao/produtos/guias/edge-functions/firewall/) diff --git a/src/content/docs/pt-br/pages/guias/guides.mdx b/src/content/docs/pt-br/pages/guias/guides.mdx index 24f01e6608..a457c25d8b 100644 --- a/src/content/docs/pt-br/pages/guias/guides.mdx +++ b/src/content/docs/pt-br/pages/guias/guides.mdx @@ -62,6 +62,7 @@ permalink: /documentacao/produtos/guias/ - [Como construir uma API RESTful com Functions e SQL Database](/pt-br/documentacao/produtos/guias/build/api-restful-edge-functions-edge-sql/) - [Como lidar com webhooks do Stripe com Functions](/pt-br/documentacao/produtos/guias/build/webhooks-stripe-functions/) - [Como executar funções serverless no Azion Console](/pt-br/documentacao/produtos/guias/funcoes-serverless/) +- [Como migrar padrões de handler em Functions](/pt-br/documentacao/produtos/guias/functions/migrar-padroes-de-handler/) ### Azion Templates