Node.js
Node.js ist eine quelloffene, plattformübergreifende JavaScript-Laufzeitumgebung.
Hono wurde ursprünglich nicht für Node.js entwickelt, kann dort aber mit einem Node.js-Adapter ebenfalls ausgeführt werden.
Info
Es funktioniert mit den Node.js-Versionen ab der 18.x-Reihe. Die konkret benötigten Versionen sind:
- 18.x => 18.14.1+
- 19.x => 19.7.0+
- 20.x => 20.0.0+
Grundsätzlich kannst du einfach die neueste Version jeder Hauptversion verwenden.
1. Einrichtung
Für Node.js steht eine Projektvorlage zur Verfügung. Starte dein Projekt mit dem Befehl „create-hono“. Wähle für dieses Beispiel die Vorlage nodejs aus.
npm create hono@latest my-appyarn create hono my-apppnpm create hono my-appbun create hono@latest my-appdeno init --npm hono my-appWechsle nach my-app und installiere die Abhängigkeiten.
cd my-app
npm icd my-app
yarncd my-app
pnpm icd my-app
bun i2. Hello World
Bearbeite src/index.ts:
import { serve } from '@hono/node-server'
import { Hono } from 'hono'
const app = new Hono()
app.get('/', (c) => c.text('Hello Node.js!'))
serve(app)Wenn du den Server geordnet herunterfahren möchtest, schreibe den Code wie folgt:
const server = serve(app)
// graceful shutdown
process.on('SIGINT', () => {
server.close()
process.exit(0)
})
process.on('SIGTERM', () => {
server.close((err) => {
if (err) {
console.error(err)
process.exit(1)
}
process.exit(0)
})
})Info
Auf Node.js umschließt serve() das Modul node:http und gibt die zugrunde liegende Serverinstanz zurück. Du musst daher selbst für das Schließen des Servers sorgen. Bun und Deno unterstützen Fetch-Handler nativ und verwalten den Server selbst, weshalb ihre Anleitungen diesen Schritt nicht enthalten.
3. Ausführen
Starte den Entwicklungsserver lokal. Öffne anschließend http://localhost:3000 in deinem Webbrowser.
npm run devyarn devpnpm devPortnummer ändern
Mit der Option port kannst du die Portnummer angeben.
serve({
fetch: app.fetch,
port: 8787,
})WebSocket
WebSocket-Unterstützung ist in @hono/node-server integriert. Installiere ws und, wenn du TypeScript verwendest, @types/ws. Erstelle anschließend einen WebSocketServer mit { noServer: true } und übergib ihn über die Option websocket an serve().
@hono/node-ws ist veraltet.
import { serve, upgradeWebSocket } from '@hono/node-server'
import { Hono } from 'hono'
import { WebSocketServer } from 'ws'
const app = new Hono()
app.get(
'/ws',
upgradeWebSocket(() => ({
onMessage(event, ws) {
ws.send(event.data)
},
}))
)
const wss = new WebSocketServer({ noServer: true })
serve({
fetch: app.fetch,
websocket: { server: wss },
})Auf die ursprünglichen Node.js-APIs zugreifen
Du kannst über c.env.incoming und c.env.outgoing auf die Node.js-APIs zugreifen.
import { Hono } from 'hono'
import { serve, type HttpBindings } from '@hono/node-server'
// or `Http2Bindings` if you use HTTP2
type Bindings = HttpBindings & {
/* ... */
}
const app = new Hono<{ Bindings: Bindings }>()
app.get('/', (c) => {
return c.json({
remoteAddress: c.env.incoming.socket.remoteAddress,
})
})
serve(app)Statische Dateien ausliefern
Mit serveStatic kannst du statische Dateien aus dem lokalen Dateisystem ausliefern. Angenommen, die Verzeichnisstruktur sieht beispielsweise so aus:
./
├── favicon.ico
├── index.ts
└── static
├── hello.txt
└── image.pngWenn eine Anfrage an den Pfad /static/* eingeht und du eine Datei unter ./static zurückgeben möchtest, kannst du Folgendes schreiben:
import { serveStatic } from '@hono/node-server/serve-static'
app.use('/static/*', serveStatic({ root: './' }))Achtung
Die Option root löst Pfade relativ zum aktuellen Arbeitsverzeichnis (process.cwd()) auf. Das Verhalten hängt also davon ab, aus welchem Verzeichnis du deinen Node.js-Prozess startest, nicht davon, wo deine Quelldatei liegt. Wenn du den Server aus einem anderen Verzeichnis startest, kann die Dateiauflösung fehlschlagen.
Für eine zuverlässige Pfadauflösung, die immer auf dasselbe Verzeichnis wie deine Quelldatei verweist, verwende import.meta.url:
import { fileURLToPath } from 'node:url'
import { serveStatic } from '@hono/node-server/serve-static'
app.use(
'/static/*',
serveStatic({ root: fileURLToPath(new URL('./', import.meta.url)) })
)Verwende die Option path, um favicon.ico im Stammverzeichnis auszuliefern:
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))Wenn eine Anfrage an /hello.txt oder /image.png eingeht und du eine Datei namens ./static/hello.txt oder ./static/image.png zurückgeben möchtest, kannst du Folgendes verwenden:
app.use('*', serveStatic({ root: './static' }))rewriteRequestPath
Wenn du http://localhost:3000/static/* auf ./statics abbilden möchtest, kannst du die Option rewriteRequestPath verwenden:
app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) =>
path.replace(/^\/static/, '/statics'),
})
)http2
Du kannst Hono auf einem Node.js-http2-Server ausführen.
Unverschlüsseltes http2
import { createServer } from 'node:http2'
const server = serve({
fetch: app.fetch,
createServer,
})Verschlüsseltes http2
import { createSecureServer } from 'node:http2'
import { readFileSync } from 'node:fs'
const server = serve({
fetch: app.fetch,
createServer: createSecureServer,
serverOptions: {
key: readFileSync('localhost-privkey.pem'),
cert: readFileSync('localhost-cert.pem'),
},
})Build und Bereitstellung
npm run buildyarn run buildpnpm run buildbun run buildInfo
Anwendungen mit einem Frontend-Framework benötigen möglicherweise Honos Vite-Plugins.
Dockerfile
Hier ist ein Beispiel für ein Node.js-Dockerfile.
FROM node:22-alpine AS base
FROM base AS builder
RUN apk add --no-cache gcompat
WORKDIR /app
COPY package*json tsconfig.json src ./
RUN npm ci && \
npm run build && \
npm prune --production
FROM base AS runner
WORKDIR /app
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 hono
COPY --from=builder --chown=hono:nodejs /app/node_modules /app/node_modules
COPY --from=builder --chown=hono:nodejs /app/dist /app/dist
COPY --from=builder --chown=hono:nodejs /app/package.json /app/package.json
USER hono
EXPOSE 3000
CMD ["node", "/app/dist/index.js"]