Voltar para o blog

AWS Lambda Web Functions na prática: API com Hono, latência e custo

Uma API que passa boa parte do tempo aguardando banco de dados não tem o mesmo perfil de uma rotina que usa CPU continuamente. Ao avaliar serverless, precisamos separar duração da requisição, processamento efetivo e memória mantida entre chamadas.

Anunciado em 12 de outubro de 2026, o AWS Lambda Web Functions introduz um modelo HTTP nativo: a aplicação inicia um servidor, atende requisições concorrentes e utiliza cobrança baseada em CPU ativa, memória e requisições. No lançamento, o serviço suporta Node.js 24 em Arm64 nas regiões Virgínia do Norte, Ohio, Oregon, Tóquio e Irlanda. Este artigo considera a documentação consultada em 13 de outubro de 2026. (aws.amazon.com)

Vamos publicar uma API com Hono e montar um experimento reproduzível. Os exemplos são didáticos; os limites e critérios de aceitação precisam ser ajustados. Nenhum resultado de benchmark é apresentado como medição realizada.

1. O que muda em relação ao Lambda tradicional

Web Functions executam servidores HTTP que escutam em uma porta. Cada ambiente pode atender várias requisições simultaneamente, e a plataforma cria ou remove ambientes conforme o tráfego, inclusive reduzindo a zero. Não é necessário converter eventos Lambda em requisições HTTP. (docs.aws.amazon.com)

Não confunda os modelos:

  • Web Functions: servidor HTTP nativo, revisões e endpoints próprios.
  • Event Functions: processamento por handler de eventos; continuam adequadas a filas e integrações orientadas a eventos.
  • Function URLs com Hono: o caminho documentado para Event Functions usa integração com o formato de eventos Lambda. Não é o deployment deste artigo. (aws.amazon.com)

Usaremos @hono/node-server para executar Hono sobre o servidor HTTP do Node.js. Esse adaptador é do runtime Node.js, não um adaptador de eventos Lambda. (hono.dev)

Os três recursos centrais

  1. Web Function: recurso que representa a aplicação.
  2. Revision: snapshot imutável de código e configuração.
  3. Endpoint: entrada HTTPS que distribui tráfego para uma ou duas revisões.

O estado Active indica disponibilidade do recurso no plano de controle, não comprova que o servidor iniciou corretamente. Sempre execute um teste HTTP após o deployment. (docs.aws.amazon.com)

2. Arquitetura e cenário corporativo

O laboratório segue este fluxo:

Cliente → Endpoint HTTPS → Revision → Ambiente Node.js → Rotas Hono

Para uma API corporativa, proponho:

Cliente → CloudFront + AWS WAF → Endpoint com IamAuth → Hono → Dependências

CloudFront fornece a camada de domínio próprio e proteção com AWS WAF, o firewall de aplicações web. O endpoint continua sendo uma entrada pública de rede; autenticação IAM restringe acesso, mas não o transforma em endpoint privado. Um conector de saída para VPC também não altera essa exposição. (docs.aws.amazon.com)

Problema: uma equipe precisa publicar uma API de catálogo com tráfego variável, sem manter servidores dedicados à camada HTTP.

Solução proposta: começar com Web Functions em uma região, validar o comportamento da API e só depois incorporar persistência e distribuição geográfica.

Implementação: catálogo estático, rota de saúde e espera assíncrona controlada para experimentação.

Resultado esperado: obter evidências sobre latência, concorrência e consumo antes de escolher a arquitetura definitiva. Não presumimos economia nem ganho de desempenho.

3. Preparar o ambiente e criar a API

Use Node.js 24, npm, curl, credenciais AWS temporárias e AWS CLI v2 com o grupo lambda-web. A identidade de deployment precisa das permissões de Web Functions e dos recursos auxiliares, incluindo iam:PassRole quando aplicável. (docs.aws.amazon.com)

aws --version
aws lambda-web help
aws sts get-caller-identity
export AWS_REGION=us-east-1

mkdir hono-web-api
cd hono-web-api
npm init -y
npm pkg set type=module
npm pkg set scripts.start='node index.js'
npm install --save-exact hono @hono/node-server

Versione package.json e package-lock.json. No pipeline, reinstale com npm ci --omit=dev e teste o pacote exato que será publicado. Se adicionar dependências nativas, faça o build para Linux Arm64, não reutilize binários da estação x86. (docs.aws.amazon.com)

Crie index.js:

import { Hono } from 'hono';
import { serve } from '@hono/node-server';
import { randomUUID } from 'node:crypto';
import { performance } from 'node:perf_hooks';
import { setTimeout as delay } from 'node:timers/promises';

const app = new Hono();
const bootId = randomUUID();
const build = process.env.APP_VERSION ?? 'local';
const address = new URL(
  `http://${process.env.AWS_LAMBDA_HTTP_ENDPOINT ?? '127.0.0.1:3000'}`
);

app.use('*', async (c, next) => {
  const requestId = randomUUID();
  const start = performance.now();
  c.header('x-request-id', requestId);
  c.header('cache-control', 'no-store');
  await next();
  console.info({
    event: 'http_request',
    appRequestId: requestId,
    bootId,
    build,
    method: c.req.method,
    status: c.res.status,
    durationMs: Number((performance.now() - start).toFixed(3))
  });
});

app.get('/health/live', (c) => c.json({ status: 'ok' }));

app.get('/v1/products/:id', (c) => {
  if (c.req.param('id') !== 'sku-1') {
    return c.json({ error: 'not_found' }, 404);
  }
  return c.json({ id: 'sku-1', name: 'Example product' });
});

if (process.env.ENABLE_LAB === 'true') {
  app.get('/lab/wait', async (c) => {
    await delay(100);
    return c.json({ simulatedWaitMs: 100 });
  });
}

app.onError((error, c) => {
  console.error({ event: 'request_error', build, name: error.name });
  return c.json({ error: 'internal_error' }, 500);
});

const server = serve({
  fetch: app.fetch,
  hostname: address.hostname,
  port: Number(address.port)
});

console.info({ event: 'app_boot', bootId, build });
process.on('SIGTERM', () => server.close());

A implementação usa a API de servidor do Hono e mantém os dados de cada chamada no escopo da requisição. bootId identifica o processo para correlação; não é um detector confiável de cold start percebido pelo cliente. (hono.dev)

O runtime carrega index.js por padrão, e o servidor precisa escutar no endereço de AWS_LAMBDA_HTTP_ENDPOINT. Não substitua esse endereço por uma porta fixa em produção. (docs.aws.amazon.com)

A rota /lab/wait simula somente espera assíncrona. Não representa conexão, autenticação, consulta ou serialização de um banco real. /health/live verifica resposta HTTP básica, não a saúde de todas as dependências.

ENABLE_LAB=true AWS_LAMBDA_HTTP_ENDPOINT=127.0.0.1:3000 npm start

Em outro terminal:

curl --fail-with-body http://127.0.0.1:3000/health/live
curl --fail-with-body http://127.0.0.1:3000/v1/products/sku-1
curl --fail-with-body http://127.0.0.1:3000/lab/wait

4. Publicar um endpoint de laboratório

O comando abaixo usa as opções documentadas de deployment e configura explicitamente runtime, timeout, concorrência e limites. Os valores são escolhas do laboratório, não recomendações universais. (docs.aws.amazon.com)

npm ci --omit=dev

aws lambda-web deploy \
  --name hono-web-api \
  --code . \
  --create \
  --runtime nodejs24.x \
  --endpoint-name dev \
  --endpoint-type HomeRegion \
  --auth-type ApplicationManaged \
  --auto-deployment-mode LatestRevision \
  --timeout-seconds 10 \
  --max-concurrency-per-environment 32 \
  --max-environments 10 \
  --rate-limit 100 \
  --env ENABLE_LAB=true \
  --env APP_VERSION=lab-v1

DOMAIN=$(aws lambda-web get-web-function-endpoint \
  --function-name hono-web-api \
  --endpoint-name dev \
  --query domainName --output text)

export BASE_URL="https://${DOMAIN}"
curl --fail-with-body -i "${BASE_URL}/health/live"

ApplicationManaged não autentica chamadas automaticamente. Como este código não implementa autenticação, o laboratório fica publicamente acessível. Use conta isolada, dados fictícios e janela curta de teste. Limitação de tráfego não substitui controle de acesso. (docs.aws.amazon.com)

No primeiro deployment, a CLI pode criar bucket de código e execution role. LatestRevision direciona o endpoint de desenvolvimento para novas revisões. Preserve o artefato e não reutilize esse modo indiscriminadamente em produção. (docs.aws.amazon.com)

5. Entender execução, concorrência e cold starts

Há três controles diferentes:

  • maxConcurrencyPerEnvironment: teto de requisições simultâneas em um ambiente.
  • maxEnvironments: teto de ambientes por endpoint em cada região.
  • rateLimit: limite de entrada em requisições por segundo.

A concorrência efetiva é dinâmica e pode ficar abaixo do teto configurado. Não há configuração de quantidade mínima ou reservada de ambientes: o endpoint pode voltar a zero. (docs.aws.amazon.com)

No exemplo, 10 × 32 = 320 é apenas um teto teórico regional, não throughput garantido. Para dimensionamento inicial, suponha 50 requisições/s com duração média de 0,2 s: isso corresponde aproximadamente a 10 requisições em andamento. Valide essa hipótese sob carga, especialmente na presença de caudas longas.

O ambiente precisa começar a escutar em até 10 segundos. Durante desligamento, a drenagem é best effort e, após SIGTERM, há até um segundo para limpeza. Não use memória local ou callbacks de encerramento como persistência de trabalho importante. (docs.aws.amazon.com)

Em produção, proponho pools pequenos e reutilizáveis. Com dez ambientes e cinco conexões por pool, o orçamento potencial é de cinquenta conexões para esse endpoint/região; some outros endpoints e consumidores antes de dimensionar o banco.

6. Medir latência sem confundir as camadas

Comece com uma chamada individual:

curl --silent --show-error --output /dev/null \
  --write-out 'status=%{http_code} dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} ttfb=%{time_starttransfer} total=%{time_total}\n' \
  "${BASE_URL}/v1/products/sku-1"

Os tempos do curl são marcos cumulativos. time_starttransfer mede até o primeiro byte; time_total vai até o término. Não some esses valores como etapas independentes. (curl.se)

O durationMs do middleware mede outro intervalo: execução da aplicação até a resposta ser preparada. Não inclui toda a rede, nem equivale ao recebimento completo pelo cliente. Também não é CPU faturável.

Teste de carga com k6

Crie load.js fora do diretório de deployment. O executor de taxa constante inicia iterações independentemente da velocidade das respostas, desde que existam usuários virtuais disponíveis. (grafana.com)

import http from 'k6/http';
import { check } from 'k6';

export const options = {
  scenarios: {
    api: {
      executor: 'constant-arrival-rate',
      rate: Number(__ENV.RATE || 50),
      timeUnit: '1s',
      duration: '2m',
      preAllocatedVUs: 50,
      maxVUs: 200
    }
  },
  thresholds: {
    http_req_failed: ['rate<0.01'],
    http_req_duration: ['p(95)<250'],
    checks: ['rate>0.99'],
    dropped_iterations: ['count==0']
  }
};

export default function () {
  const path = __ENV.TEST_PATH || '/v1/products/sku-1';
  const response = http.get(`${__ENV.BASE_URL}${path}`, {
    timeout: '5s'
  });
  check(response, { 'HTTP 200': (r) => r.status === 200 });
}
k6 run --summary-trend-stats='avg,med,p(95),p(99),max' \
  -e BASE_URL="$BASE_URL" -e RATE=50 load.js

k6 run --summary-trend-stats='avg,med,p(95),p(99),max' \
  -e BASE_URL="$BASE_URL" -e RATE=50 \
  -e TEST_PATH=/lab/wait load.js

Os thresholds são critérios hipotéticos, não desempenho prometido. Falhas nos thresholds tornam o teste reprovado; checks também precisam de threshold para participar desse resultado. (grafana.com)

Proponho três campanhas separadas:

  1. Inicialização: chamar uma revisão sem tráfego anterior e correlacionar a resposta com logs de inicialização; não classificar toda primeira chamada como cold start confirmado.
  2. Regime estável: aquecer, medir em janela própria e repetir cada cenário.
  3. Saturação: elevar a taxa gradualmente, observando erros, throttling e dependências.

Registre revisão, origem geográfica do gerador, taxa oferecida, taxa concluída, p50/p95/p99, erros e iterações descartadas. Teste também dependências reais; a AWS recomenda separar testes locais, de pacote, integração cloud, carga e resiliência. (docs.aws.amazon.com)

7. Observabilidade e interpretação do consumo

Use métricas de AWS/Lambda/Web: ExecutedRequests, HTTPResponseStatusClass5XX, EnvironmentCPUTime, EnvironmentActiveTime e EnvironmentMaxMemoryUsage. Os tempos de CPU e atividade usam milissegundos; memória máxima não é uma integral de consumo faturado. Métricas de utilização e throttling ficam em AWS/Lambda/Web/Usage, por região. (docs.aws.amazon.com)

aws logs tail /aws/lambda/web/hono-web-api --since 10m --follow

Logs são centralizados na região principal; grupos com sufixo .local preservam a visão regional. Configure retenção em ambos. A ausência inicial de logs pode ocorrer antes da primeira requisição ou enquanto a centralização acontece. (docs.aws.amazon.com)

Para operação, proponho dashboards com latência externa, erros, consumo, throttling e disponibilidade sintética. Defina um SLO, objetivo de nível de serviço, antes de automatizar promoções. Instrumente dependências com traces quando precisar separar espera no banco, chamadas externas e execução local; não presuma que os logs básicos já oferecem tracing distribuído completo. (docs.aws.amazon.com)

Não calcule CPU por requisição subtraindo process.cpuUsage() antes e depois de um await sob concorrência. A API mede CPU do processo; trabalho de outras requisições pode entrar no intervalo. Use agregação por janela e métricas de ambiente para estudar eficiência. (nodejs.org)

8. Custo e FinOps: duração não é CPU ativa

A cobrança inclui requisições, CPU ativa, memória consumida durante a existência dos ambientes e transferência. Portanto, esperar I/O não significa custo zero: a memória continua contando. A página de preços consultada apresenta, para Virgínia do Norte/Arm, US$ 0,30 por milhão de requisições, US$ 0,0000276944 por vCPU-segundo e US$ 0,0000036667 por GB-segundo de memória. Confirme região e condições antes de estimar produção. (aws.amazon.com)

Modelo de cálculo:

Custo = requisições × preço por requisição
      + CPU ativa em vCPU-segundos × preço de CPU
      + integral da memória dos ambientes em GB-segundos × preço de memória
      + rede + observabilidade + serviços auxiliares

Exemplo exclusivamente hipotético: um milhão de chamadas, consumo agregado de 5.000 vCPU-segundos e dez ambientes consumindo 0,25 GB por quatro horas cada.

Memória = 10 × 0,25 × 14.400 = 36.000 GB-segundos
Requisições = US$ 0,30
CPU = 5.000 × 0,0000276944 = US$ 0,138472
Memória = 36.000 × 0,0000036667 = US$ 0,1320012
Subtotal ilustrativo ≈ US$ 0,57

Esse subtotal exclui transferência, logs, CloudFront, WAF, banco, armazenamento e eventuais descontos. Não é orçamento de uma API completa. O exemplo usa consumo agregado assumido, não deriva memória multiplicando duração de cada requisição concorrente.

Minha proposta de FinOps é acompanhar custo por milhão de respostas bem-sucedidas, manter tráfego e retenção sob controle e reconciliar estimativas com faturamento. Não replique automaticamente a fórmula de memória alocada × duração das Event Functions. (aws.amazon.com)

9. Do laboratório à produção

TemaLaboratório deste artigoProposta para produção
AcessoPúblico, dados fictíciosIAM ou autenticação de aplicação validada
DeploymentLatestRevisionRevisões aprovadas e pesos explícitos
TestesCatálogo estático e espera simuladaDependências reais, falhas e carga
OperaçãoLogs básicosSLO, alertas, traces e runbooks
EstadoNenhum dado persistentePersistência externa e restauração testada
MudançasAtualização diretaCanary, critérios de promoção e rollback

Antes de promover, desative ENABLE_LAB, remova dados sensíveis do pacote e separe identidades de deployment, execução e invocação. A execution role deve acessar apenas os recursos necessários; segredos não devem ir para código ou logs. (docs.aws.amazon.com)

Para CloudFront, use origin access control, OAC, com IamAuth e política limitada à distribuição. Autorização da origem não substitui autorização do usuário final. Nos endpoints regionais, chamadas IAM usam SigV4; no endpoint global MultiRegion, SigV4a. (docs.aws.amazon.com)

Alta disponibilidade e restrições de rede

MultiRegion distribui por latência, não executa failover baseado em saúde. PerRegion oferece domínios regionais para roteamento externo controlado. Nenhuma opção resolve automaticamente replicação de dados, recuperação de banco ou consistência de escrita. (docs.aws.amazon.com)

Na documentação consultada, conectores de saída VPC são compatíveis somente com HomeRegion. Portanto, não combine automaticamente PostgreSQL privado via conector com um endpoint MultiRegion. Essa restrição precisa entrar na decisão arquitetural. (docs.aws.amazon.com)

Minha recomendação é começar próximo dos dados, definir objetivos de recuperação, testar restauração e só adotar múltiplas regiões com uma estratégia explícita para dependências e falhas.

10. Revisões, canary e rollback

Primeiro publique uma revisão com ENABLE_LAB=false e um APP_VERSION identificável. Valide-a no endpoint de testes e obtenha o identificador:

aws lambda-web list-web-function-revisions --function-name hono-web-api

Substitua rev-STABLE por uma revisão real e ativa. O comando cria um endpoint de produção autenticado, sem promoção automática. (docs.aws.amazon.com)

aws lambda-web create-web-function-endpoint \
  --function-name hono-web-api \
  --endpoint-name prod \
  --endpoint-type HomeRegion \
  --auth-type IamAuth \
  --auto-deployment-mode Disabled \
  --revision-weights '[{"revisionId":"rev-STABLE","weight":100}]' \
  --scaling-config '{"maxEnvironments":10}' \
  --throttle-config '{"rateLimit":100}'

Após publicar e testar outra revisão, direcione, por exemplo, 90% à estável e 10% à candidata. Os pesos precisam somar 100. Para rollback, mantenha apenas a revisão estável com peso 100; não use peso zero. (docs.aws.amazon.com)

aws lambda-web update-web-function-endpoint \
  --function-name hono-web-api \
  --endpoint-name prod \
  --auto-deployment-mode Disabled \
  --revision-weights '[{"revisionId":"rev-STABLE","weight":100}]'

aws lambda-web wait web-function-endpoint-updated \
  --function-name hono-web-api --endpoint-name prod

Preserve artefatos e versões S3 necessários à reversão. Recomendo migrações de banco compatíveis com ambas as revisões. O rollback do código não desfaz alterações de dados nem patches do runtime: Web Functions aplica atualizações do runtime automaticamente, sem pin de patch. (docs.aws.amazon.com)

11. Troubleshooting orientado a evidências

Use estes comandos para começar:

aws lambda-web get-web-function-endpoint \
  --function-name hono-web-api --endpoint-name dev

aws lambda-web get-web-function-revision \
  --function-name hono-web-api --revision-id rev-REPLACE

curl -i "${BASE_URL}/health/live"
aws logs tail /aws/lambda/web/hono-web-api.local --since 10m

Substitua rev-REPLACE por um identificador existente.

SintomaPossível causaInvestigaçãoCorreção
Active, mas HTTP falhaEntry point ou inicializaçãoLogs e endereço de escutaCorrigir pacote e publicar revisão
HTTP 429Limite regional ou do endpointHeader lambda-web-error-type e métricasAjustar carga, capacidade ou quota
HTTP 403Assinatura ou IAMAuthType, política e assinaturaCorrigir autorização, não abrir o endpoint
p99 aumenta com cargaEvent loop ou dependência saturadaCPU, traces e poolsReduzir bloqueios e controlar concorrência

O header lambda-web-error-type ajuda a distinguir falhas da plataforma das respostas da aplicação. Investigue antes de aplicar retries indiscriminados. (docs.aws.amazon.com)

12. Quando usar, alternativas e erros a evitar

Minha recomendação é avaliar Web Functions para APIs HTTP Node.js, aplicações com espera significativa por I/O e equipes que querem operar um servidor web sem administrar sua infraestrutura de execução. Para filas e processamento orientado a eventos, considere Event Functions; para requisitos fora desse modelo, avalie outra plataforma de execução. (docs.aws.amazon.com)

Como decisão arquitetural, compare também manter o serviço no ambiente de contêineres já operado pela empresa. Inclua manutenção, controles de rede, modelo de autorização, operação e custo total — não apenas CPU. Este laboratório não depende de Kubernetes e não requer manifests, probes ou requests de Pods.

Evite promover o endpoint público de laboratório, guardar estado de usuário em variáveis globais, executar trabalho síncrono pesado no event loop, confundir teto com capacidade garantida e tratar a replicação regional como recuperação completa de desastres. (docs.aws.amazon.com)

Ao terminar o laboratório:

aws lambda-web delete-web-function --function-name hono-web-api

A exclusão remove revisões e endpoints, mas não limpa automaticamente role, objetos/versionamentos S3 e grupos de logs. Revise esses recursos separadamente, sem apagar recursos compartilhados. (docs.aws.amazon.com)

Resumo prático e conclusão

O roteiro proposto é: publicar uma API mínima, comprovar resposta HTTP, medir inicialização e regime estável separadamente, observar CPU e memória agregadas, calcular custo completo e ensaiar rollback antes da produção.

Em DevOps e Platform Engineering, isso pode virar um caminho padronizado de entrega com artefatos reproduzíveis, autenticação obrigatória e promoção controlada. Para SRE, o critério de sucesso continua sendo evidência: atender ao objetivo de serviço, proteger dependências e recuperar-se de falhas com previsibilidade. Serverless muda a responsabilidade operacional; não elimina a necessidade de engenharia.

Quer aplicar isso no seu ambiente?

Agende uma conversa de 30 minutos sem compromisso. Eu olho o seu cenário e digo por onde começar.

Agendar uma conversa

Receba os novos artigos por e-mail

Um e-mail quando sair artigo novo sobre infraestrutura, backend e IA. Sem spam, cancele quando quiser.