Apidog

Plataforma de desarrollo de API colaborativa todo en uno

Diseño de API

Documentación de API

Depuración de API

Simulación de API

Prueba automatizada de API

Comenzando con Hono.js: Guía para Principiantes

Mark Ponomarev

Mark Ponomarev

Updated on May 11, 2025

En el mundo en rápida evolución del desarrollo web, encontrar el framework adecuado puede ser un desafío. Quieres algo rápido, ligero y lo suficientemente flexible como para funcionar en diferentes entornos. Aquí entra Hono, un framework web que está ganando rápidamente terreno entre los desarrolladores por su impresionante velocidad, su mínima huella y su diseño amigable para el desarrollador.

Hono (que significa "llama" 🔥 en japonés) tiene un nombre apropiado: es increíblemente rápido e ilumina tu experiencia de desarrollo con su elegante simplicidad. Ya sea que estés construyendo APIs, microservicios o aplicaciones full-stack, Hono ofrece una alternativa convincente a los frameworks más pesados.

Esta guía te guiará a través de todo lo que necesitas saber para empezar con Hono.js, desde la instalación hasta la construcción de tu primera aplicación y la comprensión de sus conceptos fundamentales.

💡
¿Quieres una excelente herramienta de prueba de API que genere hermosa Documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu equipo de desarrolladores trabaje junto con máxima productividad?

¡Apidog cumple todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
botón

¿Qué es Hono?

image-132

Hono es un framework web pequeño, simple y ultrarrápido construido sobre Estándares Web. Funciona en prácticamente cualquier runtime de JavaScript: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge y Node.js.

En esencia, Hono adopta las APIs de Estándares Web (como Request, Response y Fetch), lo que le otorga una notable portabilidad entre plataformas. Esto significa que puedes escribir tu código una vez y desplegarlo en casi cualquier lugar sin modificaciones importantes.

import { Hono } from 'hono'
const app = new Hono()

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

export default app

Este simple ejemplo demuestra la sintaxis limpia de Hono, inspirada en Express pero modernizada para el ecosistema actual de JavaScript.

¡Vamos a sumergirnos!

1. Instalar Hono.js con Bun y Configuración del Proyecto

Bun es un runtime de JavaScript moderno conocido por su velocidad y su kit de herramientas todo en uno, que incluye un gestor de paquetes, empaquetador y ejecutor de pruebas. Configurar Hono con Bun es sencillo.

Requisitos previos

Asegúrate de tener Bun instalado. Si no, puedes instalarlo siguiendo las instrucciones en el sitio web oficial de Bun.

Creando un Nuevo Proyecto Hono con Bun

La forma más rápida de iniciar un nuevo proyecto Hono con Bun es usando la herramienta de scaffolding create-hono:

bun create hono@latest my-hono-app

Este comando te pedirá que elijas una plantilla. Para esta guía, selecciona la plantilla bun.

? Which template do you want to use? › - Use arrow keys. Return to submit.
❯   bun
    cloudflare-workers
    cloudflare-pages
    deno
    fastly
    netlify
    nodejs
    vercel

Después de seleccionar la plantilla, navega a tu nuevo directorio de proyecto e instala las dependencias:

cd my-hono-app
bun install

Añadiendo Hono a un Proyecto Bun Existente

Si tienes un proyecto Bun existente, puedes añadir Hono como dependencia:

bun add hono

2. Construyendo una Aplicación Básica con Hono.js: "Hello Hono!"

Vamos a crear una aplicación simple "Hello World" (o mejor dicho, "Hello Hono!").

Dentro de tu proyecto, deberías tener un directorio src. Crea o abre src/index.ts (o index.js si prefieres JavaScript puro, aunque se recomienda TypeScript para aprovechar todos los beneficios de Hono).

// src/index.ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello Hono!')
})

export default app

Vamos a desglosar esto:

  1. Importamos la clase Hono del paquete hono.
  2. Creamos una nueva instancia de Hono, típicamente llamada app.
  3. Definimos una ruta para las solicitudes GET a la ruta raíz (/).
  4. El manejador de ruta es una función que toma un objeto Context (convencionalmente c) como argumento.
  5. c.text('Hello Hono!') crea un objeto Response con el texto plano "Hello Hono!".
  6. Finalmente, exportamos la instancia app. Para Bun, esta exportación por defecto es suficiente para que el servidor de desarrollo la detecte.

Ejecutando tu Aplicación

Para ejecutar tu aplicación en modo de desarrollo, usa el script dev definido en tu package.json (que create-hono configura por ti):

bun run dev

Esto típicamente iniciará un servidor en http://localhost:3000. Abre esta URL en tu navegador web y deberías ver "Hello Hono!".

Cambiando el Puerto

Si necesitas ejecutar tu aplicación en un puerto diferente, puedes modificar la exportación en src/index.ts:

// src/index.ts
import { Hono } from 'hono'

const app = new Hono()

app.get('/', (c) => {
  return c.text('Hello Hono on port 8080!')
})

export default {
  port: 8080, // Especifica el puerto deseado
  fetch: app.fetch,
}

Ahora, cuando ejecutes bun run dev, la aplicación se servirá en http://localhost:8080.

3. Construyendo una API Básica con Hono.js

Hono destaca en la construcción de APIs. Exploremos el enrutamiento y cómo manejar solicitudes y elaborar respuestas.

Enrutamiento

El enrutamiento en Hono es intuitivo. La instancia app tiene métodos que corresponden a los verbos HTTP (.get(), .post(), .put(), .delete(), etc.).

Ruta GET Básica

Ya hemos visto una ruta GET básica:

app.get('/hello', (c) => {
  return c.text('Hello, world!')
})

Parámetros de Ruta

Puedes definir rutas con parámetros de ruta usando la sintaxis :nombreParametro. Se puede acceder a estos parámetros a través de c.req.param('nombreParametro').

// Ejemplo: /users/123
app.get('/users/:id', (c) => {
  const userId = c.req.param('id')
  return c.text(`User ID: ${userId}`)
})

Parámetros de Consulta (Query Parameters)

Se puede acceder a los parámetros de consulta de la URL (por ejemplo, /search?q=hono) usando c.req.query('nombreConsulta').

// Ejemplo: /search?category=frameworks&limit=10
app.get('/search', (c) => {
  const category = c.req.query('category')
  const limit = c.req.query('limit')
  return c.text(`Searching in category: ${category}, Limit: ${limit}`)
})

También puedes obtener todos los parámetros de consulta como un objeto con c.req.query().

Manejo de Diferentes Métodos HTTP

Hono facilita el manejo de varios métodos HTTP para la misma ruta:

// src/index.ts
import { Hono } from 'hono'

const app = new Hono()

// GET una lista de posts
app.get('/posts', (c) => {
  // En una aplicación real, lo obtendrías de una base de datos
  const posts = [
    { id: '1', title: 'Getting Started with Hono' },
    { id: '2', title: 'Advanced Hono Techniques' },
  ];
  return c.json(posts); // Devolver respuesta JSON
})

// GET un solo post por ID
app.get('/posts/:id', (c) => {
  const id = c.req.param('id')
  // Obtener post por ID de una base de datos
  const post = { id: id, title: `Post ${id}`, content: 'This is the content...' };
  if (!post) {
    return c.notFound() // Ayudante incorporado para 404
  }
  return c.json(post)
})

// CREAR un nuevo post
app.post('/posts', async (c) => {
  const body = await c.req.json() // Parsear cuerpo JSON
  // En una aplicación real, lo guardarías en una base de datos
  console.log('Creating post:', body)
  return c.json({ message: 'Post created successfully!', data: body }, 201) // Responder con 201 Created
})

// ACTUALIZAR un post existente
app.put('/posts/:id', async (c) => {
  const id = c.req.param('id')
  const body = await c.req.json()
  // Actualizar post en la base de datos
  console.log(`Updating post ${id}:`, body)
  return c.json({ message: `Post ${id} updated successfully!`, data: body })
})

// ELIMINAR un post
app.delete('/posts/:id', (c) => {
  const id = c.req.param('id')
  // Eliminar post de la base de datos
  console.log(`Deleting post ${id}`)
  return c.json({ message: `Post ${id} deleted successfully!` })
})


export default {
  port: 3000,
  fetch: app.fetch
}

Objeto Request (c.req)

El objeto Context (c) proporciona acceso a los detalles de la solicitud a través de c.req. Las propiedades y métodos clave incluyen:

  • c.req.url: La cadena de URL completa.
  • c.req.method: El método HTTP (por ejemplo, 'GET', 'POST').
  • c.req.headers: Un objeto Headers. Accede a las cabeceras como c.req.header('Content-Type').
  • c.req.param('name'): Obtiene un parámetro de ruta.
  • c.req.query('name'): Obtiene un parámetro de consulta.
  • c.req.queries('name'): Obtiene todos los valores para un parámetro de consulta repetido como un array.
  • c.req.json(): Parsea el cuerpo de la solicitud como JSON. (Devuelve una Promise)
  • c.req.text(): Lee el cuerpo de la solicitud como texto plano. (Devuelve una Promise)
  • c.req.arrayBuffer(): Lee el cuerpo de la solicitud como un ArrayBuffer. (Devuelve una Promise)
  • c.req.formData(): Parsea el cuerpo de la solicitud como FormData. (Devuelve una Promise)
  • c.req.valid('type'): Accede a datos validados (usado con validadores, cubierto más adelante).

Objeto Response (c) y Ayudantes

El objeto Context (c) también proporciona ayudantes convenientes para crear objetos Response:

  • c.text(text, status?, headers?): Devuelve una respuesta de texto plano.
  • c.json(object, status?, headers?): Devuelve una respuesta JSON. El Content-Type se establece automáticamente a application/json.
  • c.html(html, status?, headers?): Devuelve una respuesta HTML.
  • c.redirect(location, status?): Devuelve una respuesta de redirección (estado por defecto 302).
  • c.notFound(): Devuelve una respuesta 404 Not Found.
  • c.newResponse(body, status?, headers?): Crea un nuevo objeto Response. body puede ser null, ReadableStream, ArrayBuffer, FormData, URLSearchParams o string.
  • c.header(name, value): Establece una cabecera de respuesta.

Ejemplo: Estableciendo Cabeceras Personalizadas

app.get('/custom-header', (c) => {
  c.header('X-Powered-By', 'HonoJS-Flame')
  c.header('Cache-Control', 'no-cache')
  return c.text('Check the response headers!')
})

4. Trabajando con Middleware en Hono.js

Las funciones middleware son una piedra angular de Hono (y muchos frameworks web). Son funciones que pueden procesar una solicitud antes de que llegue al manejador de ruta principal, o procesar una respuesta después de que el manejador se haya ejecutado. El middleware es excelente para tareas como registro (logging), autenticación, validación de datos, compresión, CORS y más.

La firma básica de una función middleware es async (c, next) => { ... }.

  • c: El objeto Context.
  • next: Una función a llamar para pasar el control al siguiente middleware en la cadena, o al manejador de ruta final. Debes usar await next() si quieres que se ejecuten los middleware subsiguientes o el manejador.

Usando Middleware Incorporado

Hono viene con un rico conjunto de middleware incorporado. Puedes importarlos desde hono/nombre-middleware (por ejemplo, hono/logger, hono/cors).

Para aplicar middleware a todas las rutas, usa app.use(funcionMiddleware).
Para aplicar middleware a rutas específicas, proporciona un patrón de ruta como primer argumento: app.use('/admin/*', middlewareAuth).

Ejemplo: Middleware Logger y ETag

// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { etag } from 'hono/etag'
import { prettyJSON } from 'hono/pretty-json' // Para respuestas JSON bien formateadas

const app = new Hono()

// Aplicar middleware a todas las rutas
app.use(logger()) // Registra información de solicitud y respuesta en la consola
app.use(etag())   // Añade cabeceras ETag para caché
app.use(prettyJSON()) // Formatea respuestas JSON con indentación

app.get('/', (c) => {
  return c.text('Hello with Logger and ETag!')
})

app.get('/data', (c) => {
  return c.json({ message: 'This is some data.', timestamp: Date.now() })
})

export default app

Cuando ejecutes esto y accedas a / o /data, verás registros en tu consola, y las respuestas incluirán una cabecera ETag. Las respuestas JSON de /data estarán bien formateadas.

Otro Middleware Incorporado Útil:

  • cors: Maneja el Intercambio de Recursos de Origen Cruzado (CORS).
  • basicAuth: Implementa Autenticación Básica.
  • jwt: Autenticación JWT (JSON Web Token).
  • compress: Comprime los cuerpos de respuesta.
  • cache: Implementa caché usando la API Cache.
  • secureHeaders: Añade varias cabeceras HTTP relacionadas con la seguridad.
  • bodyLimit: Limita el tamaño del cuerpo de la solicitud.

Puedes encontrar una lista completa y documentación en los documentos de Hono bajo "Middleware".

Creando Middleware Personalizado

Puedes escribir fácilmente tu propio middleware.

Ejemplo 1: Temporizador de Solicitudes Simple

// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'

const app = new Hono()

app.use(logger())

// Middleware personalizado para medir el tiempo de procesamiento de la solicitud
app.use(async (c, next) => {
  const start = Date.now()
  console.log(`Request received at: ${new Date(start).toISOString()}`)
  await next() // Llama al siguiente middleware o manejador de ruta
  const ms = Date.now() - start
  c.header('X-Response-Time', `${ms}ms`) // Añade cabecera personalizada a la respuesta
  console.log(`Request processed in ${ms}ms`)
})

app.get('/', (c) => {
  return c.text('Hello from Hono with custom timing middleware!')
})

app.get('/slow', async (c) => {
  // Simula una operación lenta
  await new Promise(resolve => setTimeout(resolve, 1500))
  return c.text('This was a slow response.')
})

export default app

Accede a / y /slow para ver los registros de tiempo y la cabecera X-Response-Time.

Ejemplo 2: Middleware de Autenticación con Clave API Simple

Este es un ejemplo muy básico para demostración. En una aplicación real, usa métodos de autenticación más robustos como JWT u OAuth, y almacena las claves API de forma segura.

// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { HTTPException } from 'hono/http-exception' // Para lanzar errores HTTP estándar

const app = new Hono()

app.use(logger())

const API_KEY = "supersecretapikey"; // En una aplicación real, almacénalo de forma segura (ej. variable de entorno)

// Middleware de Autenticación con Clave API Personalizado
const apiKeyAuth = async (c, next) => {
  const apiKeyHeader = c.req.header('X-API-KEY')
  if (apiKeyHeader && apiKeyHeader === API_KEY) {
    await next()
  } else {
    // Usando HTTPException para devolver una respuesta de error estandarizada
    throw new HTTPException(401, { message: 'Unauthorized: Invalid API Key' })
  }
}

// Aplica este middleware a un grupo específico de rutas
app.use('/api/v1/*', apiKeyAuth)

app.get('/api/v1/me', (c) => {
  return c.json({ user: 'Authenticated User', email: 'user@example.com' })
})

app.get('/public/info', (c) => {
  return c.text('This is public information, no API key needed.')
})

// Manejador de errores para HTTPException
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse()
  }
  // Para otros errores
  console.error('Unhandled error:', err)
  return c.text('Internal Server Error', 500)
})


export default app

Para probar esto:

  • curl http://localhost:3000/api/v1/me (debería devolver 401 Unauthorized)
  • curl -H "X-API-KEY: supersecretapikey" http://localhost:3000/api/v1/me (debería devolver datos de usuario)
  • curl http://localhost:3000/public/info (debería devolver información pública)

Reutilizando Middleware Personalizado con createMiddleware

Para middleware más complejo o reutilizable, puedes definirlo por separado usando createMiddleware de hono/factory. Esto también ayuda con la inferencia de tipos de TypeScript.

// src/middlewares/timing.ts
import { createMiddleware } from 'hono/factory'

export const timingMiddleware = createMiddleware(async (c, next) => {
  const start = Date.now()
  await next()
  const ms = Date.now() - start
  c.res.headers.set('X-Response-Time', `${ms}ms`) // Nota: c.res.headers.set
  console.log(` -> Processed in ${ms}ms`)
})

// src/middlewares/auth.ts
import { createMiddleware } from 'hono/factory'
import { HTTPException } from 'hono/http-exception'

const VALID_API_KEY = "anothersecretkey";

// Tipo para variables de entorno si tu middleware las necesita
type Env = {
  Variables: {
    user?: { id: string } // Ejemplo: establecer datos de usuario después de la autenticación
  }
  // Bindings: { MY_KV_NAMESPACE: KVNamespace } // Ejemplo para Cloudflare Workers
}

export const secureApiKeyAuth = createMiddleware<Env>(async (c, next) => {
  const apiKey = c.req.header('Authorization')?.replace('Bearer ', '')
  if (apiKey === VALID_API_KEY) {
    // Opcionalmente, puedes establecer variables en el contexto para manejadores posteriores
    c.set('user', { id: 'user123' })
    await next()
  } else {
    throw new HTTPException(401, { message: 'Access Denied: Secure API Key Required.'})
  }
})


// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { timingMiddleware } from './middlewares/timing' // Asumiendo que están en una carpeta middlewares
import { secureApiKeyAuth } from './middlewares/auth'

const app = new Hono()

app.use(logger())
app.use(timingMiddleware) // Aplicar a todas las rutas

app.get('/', (c) => {
  return c.text('Hello from Hono with factored middleware!')
})

app.use('/secure/data/*', secureApiKeyAuth) // Aplicar solo a /secure/data/*
app.get('/secure/data/profile', (c) => {
  // const user = c.get('user') // Acceder a la variable establecida por el middleware
  return c.json({ profileData: 'Sensitive profile information', /*user: user*/ })
})

app.get('/public/data', (c) => {
  return c.text('This is public data, no key needed.')
})

// Manejador de errores general
app.onError((err, c) => {
  if (err instanceof HTTPException) {
    return err.getResponse();
  }
  console.error('Error:', err.message);
  return c.json({ error: 'Internal Server Error', message: err.message }, 500);
});

export default app

Esta estructura hace que tu middleware sea más modular y fácil de probar de forma independiente.

5. Probando tus Aplicaciones Hono.js

Hono está diseñado para ser fácilmente testeable. Bun viene con su propio ejecutor de pruebas, bun:test, que funciona bien con Hono. También puedes usar otros ejecutores de pruebas como Vitest.

Usando bun:test (Básico)

La documentación bun.md proporciona un ejemplo básico de pruebas con bun:test:

Crea un archivo de prueba, por ejemplo, src/index.test.ts:

// src/index.test.ts
import { describe, expect, it } from 'bun:test'
import app from '.' // Importa tu app desde src/index.ts

describe('Hono Application Tests', () => {
  it('Should return 200 OK for GET /', async () => {
    const req = new Request('http://localhost/') // La URL base no importa mucho aquí
    const res = await app.fetch(req)
    expect(res.status).toBe(200)
    expect(await res.text()).toBe('Hello Hono!') // O lo que sea que devuelva tu ruta raíz
  })

  it('Should return JSON for GET /posts', async () => {
    const req = new Request('http://localhost/posts')
    const res = await app.fetch(req)
    expect(res.status).toBe(200)
    const json = await res.json()
    expect(json).toBeArray() // Asumiendo que /posts devuelve un array
    // Añade aserciones más específicas sobre el contenido JSON si es necesario
  })

  it('Should create a post for POST /posts', async () => {
    const postData = { title: 'Test Post', content: 'This is a test.' };
    const req = new Request('http://localhost/posts', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(postData),
    });
    const res = await app.fetch(req);
    expect(res.status).toBe(201); // Esperar 201 Created
    const jsonResponse = await res.json();
    expect(jsonResponse.message).toBe('Post created successfully!');
    expect(jsonResponse.data.title).toBe(postData.title);
  });
})

Para ejecutar tus pruebas:

bun test

O, si quieres ejecutar un archivo específico:

bun test src/index.test.ts

Usando el Ayudante app.request()

Hono proporciona un método conveniente app.request() que simplifica las pruebas al permitirte pasar directamente una ruta y opciones, en lugar de construir un objeto Request completo cada vez. Esto se muestra en docs/guides/testing.md.

// src/index.test.ts
import { describe, expect, it } from 'bun:test' // O importar desde 'vitest'
import app from '.' // Tu instancia de la app Hono

describe('Hono App with app.request()', () => {
  it('GET / should return "Hello Hono!"', async () => {
    const res = await app.request('/')
    expect(res.status).toBe(200)
    expect(await res.text()).toBe('Hello Hono!') // Ajusta a tu respuesta raíz real
  })

  it('GET /posts/:id should return a specific post', async () => {
    // Asumiendo que tu app tiene una ruta como app.get('/posts/:id', ...)
    // y para esta prueba, podría devolver { id: '123', title: 'Test Post 123' }
    const res = await app.request('/posts/123')
    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.id).toBe('123')
    // expect(data.title).toBe('Post 123') // O basado en la lógica de tu app
  })

  it('POST /posts should create a new post', async () => {
    const newPost = { title: 'My New Post', content: 'Awesome content here.' }
    const res = await app.request('/posts', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(newPost),
    })
    expect(res.status).toBe(201) // Asumiendo que tu POST /posts devuelve 201
    const responseData = await res.json()
    expect(responseData.message).toBe('Post created successfully!')
    expect(responseData.data.title).toBe(newPost.title)
  })

  it('GET /api/v1/me without API key should be 401', async () => {
    // Asumiendo que el middleware de clave API del ejemplo anterior está activo en esta ruta
    const res = await app.request('/api/v1/me')
    expect(res.status).toBe(401)
  })

  it('GET /api/v1/me with correct API key should be 200', async () => {
    const res = await app.request('/api/v1/me', {
      headers: {
        'X-API-KEY': 'supersecretapikey' // Usa la clave de tu middleware
      }
    })
    expect(res.status).toBe(200)
    const data = await res.json();
    expect(data.user).toBe('Authenticated User')
  })
})

Usando el Ayudante testClient() para Pruebas con Tipado Seguro

Para un tipado aún mejor, especialmente si estás usando las capacidades RPC de Hono o tienes esquemas de ruta bien definidos, Hono proporciona un ayudante testClient (desde hono/testing). Este cliente está tipado basándose en las rutas de tu aplicación Hono, dándote autocompletado y verificación de tipos en tus pruebas.

Nota Importante para la Inferencia de Tipos de testClient:
Para que testClient infiera correctamente los tipos, debes definir tus rutas usando métodos encadenados directamente en la instancia de Hono (por ejemplo, const app = new Hono().get(...).post(...)) o exportar el tipo de ruta si usas RPC. Si defines las rutas por separado (por ejemplo, const app = new Hono(); app.get(...)), la inferencia de tipos para testClient podría ser limitada.

// src/app-for-test-client.ts
// Para demostrar testClient, definamos una app con rutas encadenadas
import { Hono } from 'hono'
import { zValidator } from '@hono/zod-validator' // Ejemplo para parámetros de consulta tipados
import { z } from 'zod'

const appWithChainedRoutes = new Hono()
  .get('/search',
    zValidator('query', z.object({ q: z.string(), limit: z.coerce.number().optional() })),
    (c) => {
      const { q, limit } = c.req.valid('query')
      return c.json({ query: q, limit: limit || 10, results: [`result for ${q} 1`, `result for ${q} 2`] })
    }
  )
  .post('/submit',
    zValidator('json', z.object({ name: z.string(), email: z.string().email() })),
    async (c) => {
      const data = c.req.valid('json')
      return c.json({ message: `Received data for ${data.name}`, data }, 201)
    }
  );

export default appWithChainedRoutes;


// src/app-for-test-client.test.ts
import { describe, expect, it } from 'bun:test'
import { testClient } from 'hono/testing'
import appWithChainedRoutes from './app-for-test-client' // Importa la app

describe('Hono App with testClient', () => {
  const client = testClient(appWithChainedRoutes)

  it('GET /search should return typed results', async () => {
    const res = await client.search.$get({
      query: { q: 'hono rocks' }
    })
    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.query).toBe('hono rocks')
    expect(data.results.length).toBeGreaterThan(0)
  })

  it('GET /search with limit should respect it', async () => {
    const res = await client.search.$get({
      query: { q: 'hono with limit', limit: '5' } // Los parámetros de consulta son strings inicialmente
    })
    expect(res.status).toBe(200)
    const data = await res.json()
    expect(data.limit).toBe(5) // El validador Zod lo convirtió a número
  })

  it('POST /submit should accept valid data', async () => {
    const payload = { name: 'Test User', email: 'test@example.com' }
    // Para POST, PUT, etc. con cuerpo JSON, es client.path.$post({ json: payload })
    const res = await client.submit.$post({
      json: payload
    })
    expect(res.status).toBe(201)
    const responseData = await res.json()
    expect(responseData.message).toBe(`Received data for ${payload.name}`)
    expect(responseData.data.email).toBe(payload.email)
  });

  it('POST /submit with headers', async () => {
    const payload = { name: 'Test User Headers', email: 'testheaders@example.com' }
    const res = await client.submit.$post({
      json: payload
    }, {
      headers: {
        'X-Custom-Test-Header': 'Testing123'
      }
    })
    expect(res.status).toBe(201)
    // Normalmente tendrías una ruta/middleware que lea esta cabecera para verificar
  });
})

Para ejecutar esta prueba, asumiendo que tienes instalados @hono/zod-validator y zod (bun add @hono/zod-validator zod):

bun test src/app-for-test-client.test.ts

Este testClient proporciona una experiencia de desarrollo mucho más agradable para las pruebas, especialmente para APIs con esquemas definidos.

Conclusión

Hono ofrece un enfoque fresco para el desarrollo web con su combinación de velocidad, diseño ligero y características amigables para el desarrollador. Esta guía ha cubierto los conceptos básicos para empezar, pero hay mucho más por explorar:

  • Técnicas de enrutamiento avanzadas
  • Patrones de middleware más complejos
  • Renderizado del lado del servidor con JSX
  • Integración con bases de datos
  • APIs estilo RPC con tipado seguro usando Hono Client
  • Despliegue en varias plataformas

A medida que continúes tu viaje con Hono, consulta la documentación oficial y el repositorio de ejemplos para obtener información más detallada e inspiración.

El panorama del desarrollo web está en constante evolución, y Hono representa un enfoque moderno que adopta los Estándares Web, prioriza el rendimiento y funciona en el creciente ecosistema de runtimes de JavaScript. Ya sea que estés construyendo APIs simples o aplicaciones complejas, Hono proporciona las herramientas que necesitas sin sobrecarga innecesaria.

Empieza pequeño, experimenta y observa cómo crecen tus aplicaciones con Hono, el framework web ligero y ultrarrápido que hace honor a su nombre de fuego. 🔥

💡
¿Quieres una excelente herramienta de prueba de API que genere hermosa Documentación de API?

¿Quieres una plataforma integrada y todo en uno para que tu equipo de desarrolladores trabaje junto con máxima productividad?

¡Apidog cumple todas tus demandas y reemplaza a Postman a un precio mucho más asequible!
botón