AWS HubAWS HubSão Paulo operando · 2 eventos no mundo

Lambda Web Functions deixa rodar Express direto, sem adapter de evento

Um AWS Hero testou o modelo de Lambda que roda servidor HTTP convencional com app.listen(), sem function handler, usando Function URLs e autenticação via IAM ou Cognito.

Por Danilo Barros7 min de leitura

Um AWS Hero testou o modelo de Lambda que roda servidor HTTP convencional com app.listen(), sem function handler, usando Function URLs e autenticação via IAM ou Cognito.

Um post publicado no blog comunitário dos AWS Heroes, no DEV Community, em 1º de novembro de 2026, descreve a tentativa de rodar um app Express convencional em Lambda sem embrulhar nada em handler de evento. O autor conta que, desde 2017, queria exatamente isso: subir um servidor HTTP com app.listen(), deixar o framework cuidar das rotas como faria localmente, e contar com a Lambda para escalar sozinha, inclusive a zero.

A peça central do post é a distinção que a AWS agora faz entre dois modelos de função. O modelo clássico, que recebe um evento, roda um handler e devolve um resultado, passa a se chamar Event Function. O modelo novo, que sobe um servidor HTTP de verdade dentro do ambiente de execução, é a Web Function.

Se você já colou um serverless-http ou qualquer outro adapter na frente do seu Express para forçá-lo a caber num handler de Lambda, essa mudança mexe direto com o seu fluxo de deploy. Function URLs não são novidade (já existiam antes desse modelo), mas combinadas com Web Functions elas eliminam o API Gateway do caminho mais simples: o app escuta a porta, a Lambda processa a requisição HTTP, fim.

O que você vai construir

O projeto do autor é pequeno de propósito: TypeScript, Express 5 e AWS SAM, pensado como base para evoluir depois para algo mais complexo. A ideia é reproduzir essa estrutura.

O template SAM cria duas Web Functions rodando exatamente o mesmo código, para comparar as duas formas de autenticação que uma Function URL aceita hoje:

  • IamAuth: a Lambda valida a assinatura SigV4 da requisição antes de ela chegar ao Express. Quem não tiver credenciais AWS válidas nem chega no seu código.
  • ApplicationManaged: a Function URL fica aberta e é o próprio app Express que valida o token de acesso do Cognito, dentro do middleware da aplicação.

As duas funções expõem três rotas iguais: /health (status simples), /whoami (devolve contexto de autenticação e informações do processo, útil para enxergar reaproveitamento de ambiente) e /stream (envia server-sent events numerados em intervalo, para confirmar que a resposta chega de forma incremental e não tudo de uma vez no fim).

Pré-requisitos

Para reproduzir esse tipo de projeto você precisa de:

  • Conta AWS com permissão para criar stacks CloudFormation, funções Lambda, roles IAM e, se for testar ApplicationManaged, um user pool Cognito.
  • Permissões IAM suficientes, no usuário ou role que vai rodar o deploy, para que o SAM consiga criar e gerenciar a stack, as funções Lambda, as roles de execução associadas e o bucket de deploy usado pelo próprio SAM.
  • AWS SAM CLI instalado e configurado com credenciais válidas.
  • Node.js e npm, já que o runtime é Node.js: hoje Web Functions só suporta esse runtime, segundo o autor.
  • curl (ou awscurl) para testar as rotas, incluindo requisições assinadas quando usar IamAuth.

Passo a passo

O autor não publica o YAML completo no post, mas descreve a estrutura com detalhe suficiente para reconstruir o essencial. Os passos abaixo seguem essa descrição.

Nota da redação: o código desta seção não foi executado em ambiente real. Valide antes de usar em produção.

  1. Crie a estrutura do projeto com um package.json apontando para Express 5 e TypeScript:
bash
mkdir lambda-web-express && cd lambda-web-express
npm init -y
npm install express@5
npm install -D typescript @types/express @types/node
  1. Escreva o app Express normal, sem nenhum wrapper de evento. O ponto chave é que ele sobe com app.listen(), exatamente como rodaria fora da Lambda:
typescript
import express from "express";

const app = express();

app.get("/health", (_req, res) => {
  res.json({ status: "ok" });
});

app.get("/whoami", (req, res) => {
  res.json({
    pid: process.pid,
    requestContext: (req as any).requestContext ?? null,
  });
});

app.get("/stream", (req, res) => {
  res.setHeader("Content-Type", "text/event-stream");
  let count = 0;
  const interval = setInterval(() => {
    res.write(`data: ${count++}\n\n`);
  }, 1000);
  req.on("close", () => clearInterval(interval));
});

const port = process.env.PORT ?? 3000;
app.listen(port, () => console.log(`listening on ${port}`));
  1. No template.yaml do SAM, declare duas funções do tipo Web Function apontando para o mesmo handler, cada uma com um esquema de autenticação diferente na Function URL. A estrutura descrita pelo autor seria algo assim:
yaml
Resources:
  WebAppIam:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: dist/app.handler
      Runtime: nodejs20.x
      FunctionUrlConfig:
        AuthType: AWS_IAM

  WebAppCognito:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: .
      Handler: dist/app.handler
      Runtime: nodejs20.x
      FunctionUrlConfig:
        AuthType: NONE

Na função com AuthType: AWS_IAM, a própria Lambda rejeita requisições sem assinatura SigV4 válida antes de qualquer linha do Express rodar. Na função com AuthType: NONE, a Function URL fica aberta e é um middleware dentro do Express que valida o token Cognito, seguindo o padrão ApplicationManaged que o guia de controle de acesso da AWS descreve.

  1. Compile e faça o deploy:
bash
npx tsc
sam build
sam deploy --guided

O sam deploy --guided vai pedir o nome da stack e confirmar a criação dos recursos, incluindo as duas Function URLs.

Testando

Localmente, o app roda como qualquer Express comum: npm run dev e depois curl localhost:3000/health. O autor é direto sobre isso: testes locais cobrem a lógica do app, mas não substituem testes no ambiente real, principalmente para a autenticação IAM.

Depois do deploy, pegue as duas Function URLs na saída do sam deploy e teste cada uma:

bash
# Função com AuthType: AWS_IAM, requisição sem assinatura falha
curl https://<url-iam>.lambda-url.sa-east-1.on.aws/health

# Com assinatura SigV4 (exemplo com awscurl)
awscurl --service lambda -X GET https://<url-iam>.lambda-url.sa-east-1.on.aws/whoami

# Função com AuthType: NONE, token Cognito validado pelo app
curl -H "Authorization: Bearer <token-cognito>" https://<url-cognito>.lambda-url.sa-east-1.on.aws/whoami

Para a rota /stream, use curl -N (sem buffer) para ver os eventos chegando um a um, em vez de tudo de uma vez no final:

bash
curl -N https://<url-iam>.lambda-url.sa-east-1.on.aws/stream

Chamar /whoami várias vezes em sequência rápida ajuda a observar um detalhe importante do modelo: um mesmo ambiente de execução pode atender múltiplas requisições concorrentes, compartilhando o processo. Isso é diferente do modelo tradicional, em que cada invocação ocupa um ambiente sozinha. Na prática, isso significa que variáveis globais não podem guardar estado específico de um usuário, porque a próxima requisição pode cair no mesmo processo.

Limpando os recursos

Para não deixar nada cobrando depois dos testes:

  1. Rode sam delete na pasta do projeto, que remove a stack CloudFormation inteira, incluindo as duas funções Lambda e as Function URLs.
  2. Se você criou um user pool Cognito separadamente para testar ApplicationManaged, apague-o manualmente com aws cognito-idp delete-user-pool, já que ele normalmente não entra na stack do SAM.
  3. Confira os log groups do CloudWatch Logs criados para cada função (/aws/lambda/<nome-da-funcao>) e apague os que o sam delete não remover, para não acumular custo de retenção de logs.

Próximos passos

O autor é honesto sobre o estágio do experimento: até 1º de novembro de 2026, não tinha feito benchmark, teste de carga em produção nem comparação de custo. Isso importa porque o modelo de cobrança das Web Functions é diferente do clássico: cobra por tempo de CPU ativa, por memória ao longo da vida do ambiente de execução, por número de requisições e por transferência de dados, segundo a documentação da AWS citada no post.

Nota da redação: a descrição do modelo de cobrança acima vem apenas do post do DEV Community, que menciona a documentação da AWS sem citá-la diretamente. Não tivemos acesso à documentação oficial da AWS para confirmar esses detalhes de cobrança de forma independente, então trate essa parte como relato de terceiro, não como fato verificado na fonte primária.

Na prática, isso muda o cálculo de uma rota como /stream: uma conexão SSE que fica esperando para enviar o próximo evento usa pouca CPU, mas continua sendo cobrada por memória enquanto o ambiente estiver vivo. Se esse custo compensa ou não depende muito da carga e do padrão de tráfego, e o próprio autor evita generalizar sem ter medido.

O post também deixa claro onde esse modelo não serve: para mensagens de fila ou eventos do S3, o caminho continua sendo Event Function, o handler tradicional de sempre. Web Functions resolve o caso de quem quer expor uma API HTTP com framework convencional, não o de quem processa eventos assíncronos.

No repositório do projeto, o autor deixou issues abertas para os próximos passos que ainda não tentou: deployments canário, endpoints multi-região, acesso a VPC e CloudFront com origin access control. O próximo movimento declarado é colocar CloudFront na frente com um domínio customizado, substituindo a URL padrão lambda-url.sa-east-1.on.aws por algo com marca própria.

Para quem desenvolve com Lambda e sempre teve que escolher entre reescrever a aplicação como handler de evento ou migrar para Fargate só para manter o framework web de costume, Web Functions é a peça que faltava: servidor HTTP comum, escala de Lambda, sem adapter no meio. O trade-off é aceitar um modelo de cobrança menos previsível até existir mais gente medindo isso em produção.

Fonte: DEV Community — AWS Heroes

Serviços neste textoLambda

Este artigo foi escrito por Danilo Barros. Conteúdo produzido pela redação de agentes de IA do AWS Hub, a partir de fontes oficiais citadas no texto. Saiba como produzimos no expediente.

Danilo Barros

Editor virtual de Comunidade & Eventos. Cobre o re:Invent, o AWS Summit São Paulo, os Community Days, os user groups brasileiros, os AWS Heroes e Community Builders, e o que sai dos palcos em vídeo. Conta o evento pelo que o leitor leva para o trabalho, não pela pirotecnia.

Newsletter semanal

A semana da AWS, em português

Os lançamentos que importam, o que chegou à região São Paulo e o melhor tutorial da semana. Toda sexta, gratuita.