Erste Schritte mit Hono.js: Ein Leitfaden für Anfänger

Dieser Leitfaden führt dich durch Hono.js: Installation, erste App, Kernkonzepte.

Leo Schulz

Leo Schulz

5 June 2025

Erste Schritte mit Hono.js: Ein Leitfaden für Anfänger

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 ein großartiges API-Testtool, das wunderschöne API-Dokumentation generiert?

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!
button

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:

  1. Wir importieren die Hono-Klasse aus dem hono-Paket.
  2. Wir erstellen eine neue Instanz von Hono, die typischerweise app genannt wird.
  3. Wir definieren eine Route für GET-Anfragen an den Root-Pfad (/).
  4. Der Routen-Handler ist eine Funktion, die ein Context-Objekt (konventionell c) als Argument entgegennimmt.
  5. c.text('Hello Hono!') erstellt ein Response-Objekt mit dem Klartext "Hello Hono!".
  6. 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:

Response-Objekt (c) und Helfer

Das Context-Objekt (c) bietet auch praktische Helfer zum Erstellen von Response-Objekten:

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) => { ... }.

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:

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:

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

Explore more

Fathom-R1-14B: Fortschrittliches KI-Argumentationsmodell aus Indien

Fathom-R1-14B: Fortschrittliches KI-Argumentationsmodell aus Indien

Künstliche Intelligenz wächst rasant. FractalAIResearch/Fathom-R1-14B (14,8 Mrd. Parameter) glänzt in Mathe & Logik.

5 June 2025

Cursor 1.0 mit BugBot: KI-gestütztes Automatisierungstest-Tool ist da:

Cursor 1.0 mit BugBot: KI-gestütztes Automatisierungstest-Tool ist da:

Die Softwareentwicklung erlebt Innovationen durch KI. Cursor, ein KI-Editor, erreicht mit Version 1.0 einen Meilenstein.

5 June 2025

30+ öffentliche Web 3.0 APIs, die Sie jetzt nutzen können

30+ öffentliche Web 3.0 APIs, die Sie jetzt nutzen können

Der Aufstieg von Web 3.0: Dezentral, nutzerorientiert, transparent. APIs ermöglichen innovative dApps und Blockchain-Integration.

4 June 2025

Praktizieren Sie API Design-First in Apidog

Entdecken Sie eine einfachere Möglichkeit, APIs zu erstellen und zu nutzen