Hono no edge: APIs ultrarrápidas em Cloudflare Workers e Deno Deploy

1. O que é Hono e por que ele brilha no edge computing

Hono é um framework web minimalista criado pelo desenvolvedor japonês Yusuke Wada, projetado especificamente para rodar em runtimes de borda (edge computing). Sua filosofia central é oferecer o máximo de desempenho com o mínimo de sobrecarga — o pacote comprimido tem aproximadamente 14KB, sem dependências pesadas.

Diferentemente de Express (que carrega um ecossistema inteiro de middlewares) ou Fastify (que depende de plugins e schemas complexos), Hono foi concebido para ser executado onde cada milissegundo importa: Cloudflare Workers, Deno Deploy, Bun e Node.js. O suporte nativo a múltiplos runtimes significa que você escreve uma API uma vez e a implanta em qualquer lugar.

A principal vantagem técnica está no roteador interno. Hono usa um algoritmo de correspondência de padrões baseado em tries (árvores de prefixo), que oferece complexidade O(n) para rotas estáticas e O(1) para rotas dinâmicas — um ganho significativo em cenários com centenas de endpoints.

// Comparação de inicialização mínima
// Express: ~5MB com dependências
// Fastify: ~3MB com dependências
// Hono: ~14KB sem dependências externas

2. Configurando Hono no Cloudflare Workers

Para começar com Cloudflare Workers, você precisa do Wrangler CLI instalado. O processo é direto e orientado a TypeScript desde o início.

# Instalação do Wrangler e criação do projeto
npm install -g wrangler
wrangler init hono-worker --yes
cd hono-worker
npm install hono

Com o projeto configurado, o arquivo src/index.ts recebe a estrutura básica da API:

import { Hono } from 'hono'
import { handle } from 'hono/cloudflare-workers'

const app = new Hono()

app.get('/', (c) => c.text('Hello Hono no Edge!'))

app.get('/api/users/:id', (c) => {
  const id = c.req.param('id')
  return c.json({ userId: id, timestamp: Date.now() })
})

export default handle(app)

O deploy é igualmente simples:

wrangler deploy

Os primeiros testes de latência mostram resultados impressionantes: requisições respondidas em menos de 5ms (incluindo cold start) na maioria das regiões globais da Cloudflare. Isso ocorre porque o Worker já está "quente" após a primeira requisição, e o Hono adiciona apenas microssegundos ao tempo de resposta.

3. Hono com Deno Deploy: simplicidade e desempenho

Deno Deploy oferece uma experiência ainda mais enxuta. Não há necessidade de bundlers ou configurações complexas — você importa o Hono diretamente de uma URL.

# Inicialização do projeto Deno
deno init hono-deno
cd hono-deno

O arquivo main.ts fica assim:

import { Hono } from 'https://deno.land/x/hono@v3.11.7/mod.ts'
import { serve } from 'https://deno.land/x/hono@v3.11.7/deno.ts'

const app = new Hono()

app.get('/', (c) => c.text('Hono no Deno Deploy!'))

app.post('/api/data', async (c) => {
  const body = await c.req.json()
  return c.json({ received: body, processed: true })
})

serve(app, { port: 8000 })

Para deploy, use o dashboard do Deno Deploy ou o CLI:

deployctl deploy --project=hono-demo main.ts

Comparando cold starts: Cloudflare Workers tendem a ter cold starts ligeiramente menores (cerca de 1-2ms) para Workers simples, enquanto Deno Deploy apresenta cold starts de 3-5ms. Porém, Deno compensa com uma experiência de desenvolvimento mais fluida, sem necessidade de bundlers ou configurações de build.

4. Recursos avançados para APIs ultrarrápidas

Validação com Zod

Hono integra-se perfeitamente com Zod para validação de dados no edge:

import { z } from 'zod'
import { zValidator } from 'hono/zod-validator'

const schema = z.object({
  name: z.string().min(3),
  email: z.string().email()
})

app.post('/api/users', zValidator('json', schema), async (c) => {
  const user = c.req.valid('json')
  return c.json({ success: true, user })
})

Cache inteligente

Para reduzir ainda mais a latência, use cabeçalhos Cache-Control e Workers KV:

app.get('/api/products', async (c) => {
  const cacheKey = 'products-list'
  const cached = await c.env.KV.get(cacheKey, 'json')

  if (cached) {
    return c.json(cached, 200, {
      'Cache-Control': 'public, max-age=3600',
      'X-Cache': 'HIT'
    })
  }

  const products = await fetchProducts()
  await c.env.KV.put(cacheKey, JSON.stringify(products), { expirationTtl: 3600 })

  return c.json(products, 200, {
    'Cache-Control': 'public, max-age=3600',
    'X-Cache': 'MISS'
  })
})

Streaming e Server-Sent Events

Hono suporta streaming nativo, ideal para APIs em tempo real:

app.get('/events', (c) => {
  return c.stream(async (stream) => {
    for (let i = 0; i < 10; i++) {
      await stream.write(`data: Evento ${i}\n\n`)
      await stream.sleep(1000)
    }
  })
})

5. Padrões de roteamento e middleware para edge

Hono oferece roteamento flexível com suporte a parâmetros dinâmicos e expressões regulares:

// Roteamento dinâmico
app.get('/api/users/:id/posts/:postId', (c) => {
  const { id, postId } = c.req.param()
  return c.json({ user: id, post: postId })
})

// Roteamento com regex
app.get('/api/items/:id{[0-9]+}', (c) => {
  const id = c.req.param('id')
  return c.json({ itemId: parseInt(id) })
})

Middlewares globais e por rota permitem implementar autenticação, CORS e rate limiting sem esforço:

import { cors } from 'hono/cors'
import { bearerAuth } from 'hono/bearer-auth'

// Middleware global
app.use('*', cors({
  origin: 'https://meudominio.com',
  allowMethods: ['GET', 'POST']
}))

// Middleware por rota
app.use('/api/admin/*', bearerAuth({ token: 'secret-token' }))

// Rate limiting simples
app.use('/api/*', async (c, next) => {
  const ip = c.req.header('CF-Connecting-IP') || 'unknown'
  const count = await c.env.KV.get(`rate:${ip}`, 'json') || 0

  if (count > 100) {
    return c.json({ error: 'Rate limit exceeded' }, 429)
  }

  await c.env.KV.put(`rate:${ip}`, String(count + 1), { expirationTtl: 60 })
  await next()
})

Tratamento de erros padronizado:

app.onError((err, c) => {
  console.error(`Erro: ${err.message}`)
  return c.json({
    error: 'Internal Server Error',
    message: err.message
  }, 500)
})

app.notFound((c) => {
  return c.json({ error: 'Not Found' }, 404)
})

6. Integração com serviços de borda (KV, R2, D1)

Workers KV para sessões

app.post('/api/login', async (c) => {
  const { username, password } = await c.req.json()
  const user = await authenticate(username, password)

  if (!user) {
    return c.json({ error: 'Invalid credentials' }, 401)
  }

  const sessionId = crypto.randomUUID()
  await c.env.KV.put(`session:${sessionId}`, JSON.stringify(user), {
    expirationTtl: 86400 // 24 horas
  })

  return c.json({ sessionId })
})

R2 para upload de arquivos

app.post('/api/upload', async (c) => {
  const formData = await c.req.formData()
  const file = formData.get('file') as File

  if (!file) {
    return c.json({ error: 'No file provided' }, 400)
  }

  const key = `uploads/${Date.now()}-${file.name}`
  await c.env.R2.put(key, await file.arrayBuffer(), {
    httpMetadata: { contentType: file.type }
  })

  return c.json({ url: `https://cdn.exemplo.com/${key}` })
})

D1 (SQLite serverless)

app.get('/api/users', async (c) => {
  const { results } = await c.env.DB.prepare(
    'SELECT id, name, email FROM users LIMIT 10'
  ).all()

  return c.json(results)
})

7. Testes, monitoramento e boas práticas

Testes unitários com Vitest para Workers:

// tests/api.test.ts
import { describe, it, expect } from 'vitest'
import app from '../src/index'

describe('API Hono', () => {
  it('deve retornar saudação', async () => {
    const res = await app.request('/')
    expect(res.status).toBe(200)
    expect(await res.text()).toBe('Hello Hono no Edge!')
  })

  it('deve validar dados do usuário', async () => {
    const res = await app.request('/api/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: 'Jo', email: 'invalido' })
    })
    expect(res.status).toBe(400)
  })
})

Monitoramento com Cloudflare Observability:

// Adicione métricas personalizadas
app.use('*', async (c, next) => {
  const start = Date.now()
  await next()
  const duration = Date.now() - start

  c.executionCtx.waitUntil(
    c.env.METRICS.writeDataPoint({
      blobs: [c.req.method, c.req.path],
      doubles: [duration]
    })
  )
})

Boas práticas para deploy contínuo:

  1. Versionamento semântico das APIs (/v1/, /v2/)
  2. Testes automatizados antes do deploy
  3. Deploy gradual com Cloudflare Workers (staging → production)
  4. Monitoramento de erros com Sentry ou Datadog

8. Casos de uso reais e quando evitar Hono no edge

Casos ideais

  • APIs de geolocalização: Workers respondem em milissegundos na borda mais próxima
  • Webhooks: Processamento rápido de eventos sem overhead de servidor
  • Autenticação descentralizada: Verificação de tokens JWT sem estado
  • APIs de consulta rápida: Catálogos de produtos, preços em tempo real

Limitações

  • Workloads pesados de CPU: Processamento de imagens, machine learning ou criptografia intensa
  • Dependências nativas: Bibliotecas que exigem binários compilados (ex: sharp, bcrypt)
  • Conexões longas: WebSockets persistentes em grande escala podem ser custosos

Alternativas

  • Fastify: Para APIs monolíticas que exigem ecossistema rico de plugins
  • NestJS: Quando a arquitetura modular e injeção de dependência são requisitos
  • Servidores tradicionais: Para workloads que ultrapassam os limites de CPU/memória do edge (10ms de CPU, 128MB RAM)

Referências