In der sich schnell entwickelnden Welt der Webentwicklung kann die Suche nach dem richtigen Framework eine Herausforderung sein. Sie wollen etwas Schnelles, Leichtgewichtiges und Flexibles, das in verschiedenen Umgebungen funktioniert. Betreten Sie Hono – ein Web-Framework, das bei Entwicklern aufgrund seiner beeindruckenden Geschwindigkeit, seines minimalen Footprints und seines entwicklerfreundlichen Designs schnell an Bedeutung gewinnt.
Hono (was auf Japanisch „Flamme“ 🔥 bedeutet) ist treffend benannt – es ist rasend schnell und erhellt Ihre Entwicklungserfahrung mit seiner eleganten Einfachheit. Egal, ob Sie APIs, Microservices oder Full-Stack-Anwendungen erstellen, Hono bietet eine überzeugende Alternative zu schwereren Frameworks.
Dieser Leitfaden führt Sie durch alles, was Sie wissen müssen, um mit Hono.js zu beginnen, von der Installation über das Erstellen Ihrer ersten Anwendung bis hin zum Verständnis seiner Kernkonzepte.
Benötigen Sie eine integrierte All-in-One-Plattform für Ihr Entwicklerteam, um mit maximaler Produktivität zusammenzuarbeiten?
Apidog liefert alle Ihre Anforderungen und ersetzt Postman zu einem viel günstigeren Preis!
Was ist Hono?

Hono ist ein kleines, einfaches und ultraschnelles Web-Framework, das auf Web Standards basiert. Es funktioniert auf praktisch jeder JavaScript-Runtime: Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Netlify, AWS Lambda, Lambda@Edge und Node.js.
Im Kern umfasst Hono die Web Standards APIs (wie Request, Response und Fetch), was ihm bemerkenswerte Portabilität über Plattformen hinweg verleiht. Dies bedeutet, dass Sie Ihren Code einmal schreiben und ihn fast überall ohne größere Änderungen bereitstellen können.
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hono!'))
export default app
Dieses einfache Beispiel demonstriert Honos saubere Syntax, inspiriert von Express, aber für das heutige JavaScript-Ökosystem modernisiert.
Lass uns eintauchen!
1. Installieren Sie Hono.js mit Bun und Projekt-Setup
Bun ist eine moderne JavaScript-Runtime, die für ihre Geschwindigkeit und ihr All-in-One-Toolkit bekannt ist, einschließlich eines Paketmanagers, Bundlers und Testrunners. Das Einrichten von Hono mit Bun ist unkompliziert.
Voraussetzungen
Stellen Sie sicher, dass Sie Bun installiert haben. Wenn nicht, können Sie es installieren, indem Sie die Anweisungen auf der offiziellen Bun-Website befolgen.
Erstellen eines neuen Hono-Projekts mit Bun
Der schnellste Weg, um ein neues Hono-Projekt mit Bun zu starten, ist die Verwendung des create-hono
-Scaffolding-Tools:
bun create hono@latest my-hono-app
Dieser Befehl fordert Sie auf, eine Vorlage auszuwählen. Wählen Sie für diesen Leitfaden die Vorlage bun
aus.
? Which template do you want to use? › - Use arrow keys. Return to submit.
❯ bun
cloudflare-workers
cloudflare-pages
deno
fastly
netlify
nodejs
vercel
Navigieren Sie nach der Auswahl der Vorlage in Ihr neues Projektverzeichnis und installieren Sie die Abhängigkeiten:
cd my-hono-app
bun install
Hinzufügen von Hono zu einem bestehenden Bun-Projekt
Wenn Sie ein bestehendes Bun-Projekt haben, können Sie Hono als Abhängigkeit hinzufügen:
bun add hono
2. Erstellen einer einfachen Anwendung mit Hono.js: "Hello Hono!"
Lassen Sie uns eine einfache "Hello World"-Anwendung (oder besser gesagt "Hello Hono!") erstellen.
In Ihrem Projekt sollten Sie ein src
-Verzeichnis haben. Erstellen oder öffnen Sie src/index.ts
(oder index.js
, wenn Sie reines JavaScript bevorzugen, obwohl TypeScript für die vollen Vorteile von Hono empfohlen wird).
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => {
return c.text('Hello Hono!')
})
export default app
Lassen Sie uns dies aufschlüsseln:
- Wir importieren die
Hono
-Klasse aus demhono
-Paket. - Wir erstellen eine neue Instanz von
Hono
, die typischerweiseapp
genannt wird. - Wir definieren eine Route für
GET
-Anfragen an den Root-Pfad (/
). - Der Routen-Handler ist eine Funktion, die ein
Context
-Objekt (konventionellc
) als Argument entgegennimmt. c.text('Hello Hono!')
erstellt ein Response-Objekt mit dem Klartext "Hello Hono!".- Schließlich exportieren wir die
app
-Instanz. Für Bun reicht dieser Standardexport aus, damit der Entwicklungsserver ihn abrufen kann.
Ausführen Ihrer Anwendung
Um Ihre Anwendung im Entwicklungsmodus auszuführen, verwenden Sie das dev
-Skript, das in Ihrer package.json
definiert ist (das create-hono
für Sie einrichtet):
bun run dev
Dadurch wird in der Regel ein Server unter http://localhost:3000
gestartet. Öffnen Sie diese URL in Ihrem Webbrowser, und Sie sollten "Hello Hono!" sehen.
Ändern des Ports
Wenn Sie Ihre Anwendung auf einem anderen Port ausführen müssen, können Sie den Export in src/index.ts
ändern:
// 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, // Specify your desired port
fetch: app.fetch,
}
Wenn Sie jetzt bun run dev
ausführen, wird die Anwendung unter http://localhost:8080
bereitgestellt.
3. Erstellen einer einfachen API mit Hono.js
Hono zeichnet sich durch das Erstellen von APIs aus. Lassen Sie uns das Routing untersuchen und wie man Anfragen verarbeitet und Antworten erstellt.
Routing
Das Routing in Hono ist intuitiv. Die app
-Instanz verfügt über Methoden, die den HTTP-Verben entsprechen (.get()
, .post()
, .put()
, .delete()
usw.).
Einfache GET-Route
Wir haben bereits eine einfache GET-Route gesehen:
app.get('/hello', (c) => {
return c.text('Hello, world!')
})
Pfadparameter
Sie können Routen mit Pfadparametern mithilfe der Syntax :paramName
definieren. Auf diese Parameter kann über c.req.param('paramName')
zugegriffen werden.
// Example: /users/123
app.get('/users/:id', (c) => {
const userId = c.req.param('id')
return c.text(`User ID: ${userId}`)
})
Abfrageparameter
Auf Abfrageparameter aus der URL (z. B. /search?q=hono
) kann mithilfe von c.req.query('queryName')
zugegriffen werden.
// Example: /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}`)
})
Sie können auch alle Abfrageparameter als Objekt mit c.req.query()
abrufen.
Umgang mit verschiedenen HTTP-Methoden
Hono macht es einfach, verschiedene HTTP-Methoden für denselben Pfad zu verarbeiten:
// src/index.ts
import { Hono } from 'hono'
const app = new Hono()
// GET a list of posts
app.get('/posts', (c) => {
// In a real app, you'd fetch this from a database
const posts = [
{ id: '1', title: 'Getting Started with Hono' },
{ id: '2', title: 'Advanced Hono Techniques' },
];
return c.json(posts); // Return JSON response
})
// GET a single post by ID
app.get('/posts/:id', (c) => {
const id = c.req.param('id')
// Fetch post by ID from a database
const post = { id: id, title: `Post ${id}`, content: 'This is the content...' };
if (!post) {
return c.notFound() // Built-in helper for 404
}
return c.json(post)
})
// CREATE a new post
app.post('/posts', async (c) => {
const body = await c.req.json() // Parse JSON body
// In a real app, you'd save this to a database
console.log('Creating post:', body)
return c.json({ message: 'Post created successfully!', data: body }, 201) // Respond with 201 Created
})
// UPDATE an existing post
app.put('/posts/:id', async (c) => {
const id = c.req.param('id')
const body = await c.req.json()
// Update post in database
console.log(`Updating post ${id}:`, body)
return c.json({ message: `Post ${id} updated successfully!`, data: body })
})
// DELETE a post
app.delete('/posts/:id', (c) => {
const id = c.req.param('id')
// Delete post from database
console.log(`Deleting post ${id}`)
return c.json({ message: `Post ${id} deleted successfully!` })
})
export default {
port: 3000,
fetch: app.fetch
}
Request-Objekt (c.req
)
Das Context
-Objekt (c
) bietet über c.req
Zugriff auf die Anforderungsdetails. Zu den wichtigsten Eigenschaften und Methoden gehören:
c.req.url
: Die vollständige URL-Zeichenfolge.c.req.method
: Die HTTP-Methode (z. B. 'GET', 'POST').c.req.headers
: Ein Headers-Objekt. Greifen Sie auf Header wiec.req.header('Content-Type')
zu.c.req.param('name')
: Abrufen eines Pfadparameters.c.req.query('name')
: Abrufen eines Abfrageparameters.c.req.queries('name')
: Abrufen aller Werte für einen wiederholten Abfrageparameter als Array.c.req.json()
: Analysiert den Anforderungstext als JSON. (Gibt ein Promise zurück)c.req.text()
: Liest den Anforderungstext als Klartext. (Gibt ein Promise zurück)c.req.arrayBuffer()
: Liest den Anforderungstext als ArrayBuffer. (Gibt ein Promise zurück)c.req.formData()
: Analysiert den Anforderungstext als FormData. (Gibt ein Promise zurück)c.req.valid('type')
: Zugriff auf validierte Daten (wird mit Validatoren verwendet, die später behandelt werden).
Response-Objekt (c
) und Helfer
Das Context
-Objekt (c
) bietet auch praktische Helfer zum Erstellen von Response
-Objekten:
c.text(text, status?, headers?)
: Gibt eine Klartextantwort zurück.c.json(object, status?, headers?)
: Gibt eine JSON-Antwort zurück. DerContent-Type
wird automatisch aufapplication/json
gesetzt.c.html(html, status?, headers?)
: Gibt eine HTML-Antwort zurück.c.redirect(location, status?)
: Gibt eine Redirect-Antwort zurück (Standardstatus 302).c.notFound()
: Gibt eine 404 Not Found-Antwort zurück.c.newResponse(body, status?, headers?)
: Erstellt ein neuesResponse
-Objekt.body
kannnull
,ReadableStream
,ArrayBuffer
,FormData
,URLSearchParams
oderstring
sein.c.header(name, value)
: Setzt einen Antwort-Header.
Beispiel: Festlegen benutzerdefinierter Header
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. Arbeiten mit Middleware in Hono.js
Middleware-Funktionen sind ein Eckpfeiler von Hono (und vielen Web-Frameworks). Sie sind Funktionen, die eine Anfrage verarbeiten können, bevor sie den Hauptrouten-Handler erreicht, oder eine Antwort verarbeiten können, nachdem der Handler ausgeführt wurde. Middleware eignet sich hervorragend für Aufgaben wie Protokollierung, Authentifizierung, Datenvalidierung, Komprimierung, CORS und mehr.
Die grundlegende Signatur einer Middleware-Funktion ist async (c, next) => { ... }
.
c
: Das Context-Objekt.next
: Eine Funktion, die aufgerufen werden soll, um die Steuerung an die nächste Middleware in der Kette oder an den endgültigen Routen-Handler zu übergeben. Sie müssenawait next()
verwenden, wenn Sie möchten, dass nachfolgende Middleware oder der Handler ausgeführt werden.
Verwenden von integrierter Middleware
Hono wird mit einer umfangreichen Reihe von integrierter Middleware geliefert. Sie können sie von hono/middleware-name
importieren (z. B. hono/logger
, hono/cors
).
Um Middleware auf alle Routen anzuwenden, verwenden Sie app.use(middlewareFunction)
.
Um Middleware auf bestimmte Routen anzuwenden, geben Sie ein Pfadmuster als erstes Argument an: app.use('/admin/*', authMiddleware)
.
Beispiel: Logger und ETag-Middleware
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { etag } from 'hono/etag'
import { prettyJSON } from 'hono/pretty-json' // For nicely formatted JSON responses
const app = new Hono()
// Apply middleware to all routes
app.use(logger()) // Logs request and response info to the console
app.use(etag()) // Adds ETag headers for caching
app.use(prettyJSON()) // Formats JSON responses with indentation
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
Wenn Sie dies ausführen und auf /
oder /data
zugreifen, sehen Sie Protokolle in Ihrer Konsole, und Antworten enthalten einen ETag
-Header. JSON-Antworten von /data
werden gut formatiert.
Andere nützliche integrierte Middleware:
cors
: Behandelt Cross-Origin Resource Sharing.basicAuth
: Implementiert die einfache Authentifizierung.jwt
: JWT (JSON Web Token)-Authentifizierung.compress
: Komprimiert Antworttexte.cache
: Implementiert das Caching mithilfe der Cache-API.secureHeaders
: Fügt verschiedene sicherheitsrelevante HTTP-Header hinzu.bodyLimit
: Begrenzt die Größe des Anforderungstexts.
Eine vollständige Liste und Dokumentation finden Sie in den Hono-Dokumenten unter "Middleware".
Erstellen benutzerdefinierter Middleware
Sie können ganz einfach Ihre eigene Middleware schreiben.
Beispiel 1: Einfacher Anforderungs-Timer
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
const app = new Hono()
app.use(logger())
// Custom middleware to measure request processing time
app.use(async (c, next) => {
const start = Date.now()
console.log(`Request received at: ${new Date(start).toISOString()}`)
await next() // Call the next middleware or route handler
const ms = Date.now() - start
c.header('X-Response-Time', `${ms}ms`) // Add custom header to the response
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) => {
// Simulate a slow operation
await new Promise(resolve => setTimeout(resolve, 1500))
return c.text('This was a slow response.')
})
export default app
Greifen Sie auf /
und /slow
zu, um die Timing-Protokolle und den X-Response-Time
-Header anzuzeigen.
Beispiel 2: Einfache API-Schlüssel-Authentifizierungs-Middleware
Dies ist ein sehr einfaches Beispiel zur Demonstration. Verwenden Sie in einer realen Anwendung robustere Authentifizierungsmethoden wie JWT oder OAuth und speichern Sie API-Schlüssel sicher.
// src/index.ts
import { Hono } from 'hono'
import { logger } from 'hono/logger'
import { HTTPException } from 'hono/http-exception' // For throwing standard HTTP errors
const app = new Hono()
app.use(logger())
const API_KEY = "supersecretapikey"; // In a real app, store this securely (e.g., env variable)
// Custom API Key Authentication Middleware
const apiKeyAuth = async (c, next) => {
const apiKeyHeader = c.req.header('X-API-KEY')
if (apiKeyHeader && apiKeyHeader === API_KEY) {
await next()
} else {
// Using HTTPException to return a standardized error response
throw new HTTPException(401, { message: 'Unauthorized: Invalid API Key' })
}
}
// Apply this middleware to a specific group of routes
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.')
})
// Error handler for HTTPException
app.onError((err, c) => {
if (err instanceof HTTPException) {
return err.getResponse()
}
// For other errors
console.error('Unhandled error:', err)
return c.text('Internal Server Error', 500)
})
export default app
So testen Sie dies:
curl http://localhost:3000/api/v1/me
(sollte 401 Unauthorized zurückgeben)curl -H "X-API-KEY: supersecretapikey" http://localhost:3000/api/v1/me
(sollte Benutzerdaten zurückgeben)curl http://localhost:3000/public/info
(sollte öffentliche Informationen zurückgeben)
Wiederverwenden benutzerdefinierter Middleware mit createMiddleware
Für komplexere oder wiederverwendbare Middleware können Sie diese separat mithilfe von createMiddleware
von hono/factory
definieren. Dies hilft auch bei der TypeScript-Typinferenz.
// 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`) // Note: 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";
// Type for environment variables if your middleware needs them
type Env = {
Variables: {
user?: { id: string } // Example: set user data after auth
}
// Bindings: { MY_KV_NAMESPACE: KVNamespace } // Example for Cloudflare Workers
}
export const secureApiKeyAuth = createMiddleware<Env>(async (c, next) => {
const apiKey = c.req.header('Authorization')?.replace('Bearer ', '')
if (apiKey === VALID_API_KEY) {
// Optionally, you can set variables in the context for downstream handlers
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' // Assuming they are in a middlewares folder
import { secureApiKeyAuth } from './middlewares/auth'
const app = new Hono()
app.use(logger())
app.use(timingMiddleware) // Apply to all routes
app.get('/', (c) => {
return c.text('Hello from Hono with factored middleware!')
})
app.use('/secure/data/*', secureApiKeyAuth) // Apply only to /secure/data/*
app.get('/secure/data/profile', (c) => {
// const user = c.get('user') // Access variable set by 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.')
})
// General error handler
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
Diese Struktur macht Ihre Middleware modularer und einfacher unabhängig zu testen.
5. Testen Ihrer Hono.js-Anwendungen
Hono ist so konzipiert, dass es einfach zu testen ist. Bun wird mit seinem eigenen Testrunner bun:test
geliefert, der gut mit Hono funktioniert. Sie können auch andere Testrunner wie Vitest verwenden.
Verwenden von bun:test
(Basic)
Die Dokumentation bun.md
enthält ein einfaches Beispiel für das Testen mit bun:test
:
Erstellen Sie eine Testdatei, z. B. src/index.test.ts
:
// src/index.test.ts
import { describe, expect, it } from 'bun:test'
import app from '.' // Imports your app from src/index.ts
describe('Hono Application Tests', () => {
it('Should return 200 OK for GET /', async () => {
const req = new Request('http://localhost/') // Base URL doesn't matter much here
const res = await app.fetch(req)
expect(res.status).toBe(200)
expect(await res.text()).toBe('Hello Hono!') // Or whatever your root route returns
})
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() // Assuming /posts returns an array
// Add more specific assertions about the JSON content if needed
})
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); // Expect 201 Created
const jsonResponse = await res.json();
expect(jsonResponse.message).toBe('Post created successfully!');
expect(jsonResponse.data.title).toBe(postData.title);
});
})
So führen Sie Ihre Tests aus:
bun test
Oder, wenn Sie eine bestimmte Datei ausführen möchten:
bun test src/index.test.ts
Verwenden des app.request()
-Helfer
Hono bietet eine praktische app.request()
-Methode, die das Testen vereinfacht, indem Sie direkt einen Pfad und Optionen übergeben können, anstatt jedes Mal ein vollständiges Request
-Objekt zu erstellen. Dies wird in docs/guides/testing.md
gezeigt.
// src/index.test.ts
import { describe, expect, it } from 'bun:test' // Or import from 'vitest'
import app from '.' // Your Hono app instance
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!') // Adjust to your actual root response
})
it('GET /posts/:id should return a specific post', async () => {
// Assuming your app has a route like app.get('/posts/:id', ...)
// and for this test, it might return { 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') // Or based on your app's logic
})
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) // Assuming your POST /posts returns 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 () => {
// Assuming the API key middleware from earlier example is active on this route
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' // Use the key from your middleware
}
})
expect(res.status).toBe(200)
const data = await res.json();
expect(data.user).toBe('Authenticated User')
})
})
Verwenden des testClient()
-Helfer für typsicheres Testen
Für noch bessere Typsicherheit, insbesondere wenn Sie die RPC-Funktionen von Hono verwenden oder gut definierte Routenschemata haben, bietet Hono einen testClient
-Helfer (von hono/testing
). Dieser Client wird basierend auf den Routen Ihrer Hono-Anwendung typisiert, wodurch Sie Autovervollständigung und Typprüfung in Ihren Tests erhalten.
Wichtiger Hinweis für die testClient
-Typinferenz:
Damit testClient
Typen korrekt ableiten kann, müssen Sie Ihre Routen mithilfe von verketteten Methoden direkt auf der Hono
-Instanz definieren (z. B. const app = new Hono().get(...).post(...)
) oder den Routentyp exportieren, wenn Sie RPC verwenden. Wenn Sie Routen separat definieren (z. B. const app = new Hono(); app.get(...)
), kann die Typinferenz für testClient
eingeschränkt sein.
// src/app-for-test-client.ts
// To demonstrate testClient, let's define an app with chained routes
import { Hono } from 'hono'
import { zValidator } from '@hono