Node.js
Node.js はオープンソースでクロスプラットフォームの JavaScript ランタイム環境です。
Hono は Node.js 向けに設計されたわけではありませんが、 Node.js Adapter を使うと Node.js でも実行できます。
INFO
Node.js 18.x 以上で動作します。 具体的に必要な Node.js のバージョンは以下の通りです:
- 18.x => 18.14.1+
- 19.x => 19.7.0+
- 20.x => 20.0.0+
具体的には、各メジャーリリースの最新バージョンを使用するだけです。
1. セットアップ
Node.js のスターターが利用可能です。 "create-hono" コマンドでプロジェクトを開始してください。 この例では nodejs テンプレートを選択してください。
npm create hono@latest my-appyarn create hono my-apppnpm create hono my-appbun create hono@latest my-appdeno init --npm hono my-appmy-app に移動して依存パッケージをインストールします。
cd my-app
npm icd my-app
yarncd my-app
pnpm icd my-app
bun i2. Hello World
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)サーバをグレースフルシャットダウンしたい場合は、次のように記述します:
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
Node.js では、serve() は node:http モジュールをラップし、基盤となるサーバーインスタンスを返すため、閉じるのはあなた次第です。 Bun と Deno は Fetch ハンドラーをネイティブに提供し、サーバー自身を管理するため、それらのガイドにはこの手順は含まれていません。
3. 実行
開発サーバーをローカルで起動し、 Web ブラウザで http://localhost:3000 にアクセスします。
npm run devyarn devpnpm devポート番号の変更
port オプションでポート番号を指定できます。
serve({
fetch: app.fetch,
port: 8787,
})WebSocket
WebSocket サポートは @hono/node-server に組み込まれています。 ws をインストールし、 TypeScript を使用している場合は @types/ws もインストールします。 次に、 { noServer: true } を指定して WebSocketServer を生成し、 websocket オプションを使用して serve() に渡します。
@hono/node-ws は非推奨です。
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 },
})生の Node.js API へのアクセス
Node.js API は c.env.incoming と c.env.outgoing からアクセスできます。
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)静的ファイルの配信
serveStatic を使うことでローカルファイルシステムから静的ファイルを配信できます。 例えば、ディレクトリ構造が次のような場合を考えます:
./
├── favicon.ico
├── index.ts
└── static
├── hello.txt
└── image.pngパス /static/* へのリクエストが来て、 ./static 配下のファイルを返したい場合、次のように記述できます:
import { serveStatic } from '@hono/node-server/serve-static'
app.use('/static/*', serveStatic({ root: './' }))WARNING
root オプションは、カレントの作業ディレクトリ (process.cwd()) からの相対パスで解決します。 これは、サーバを実行している場所ではなく、 ソースファイルが置かれている場所によって動作が異なる ことを意味します。 異なるディレクトリからサーバを起動した場合、ファイル解決が失敗する可能性があります。
ソースファイルと常に同じディレクトリを指す信頼できるパス解決を行うには、 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)) })
)ディレクトリルートの favicon.ico を配信するには path オプションを使用します:
app.use('/favicon.ico', serveStatic({ path: './favicon.ico' }))パス /hello.txt や /image.png へのリクエストが来て、 ./static/hello.txt や ./static/image.png という名前のファイルを返したい場合、次のように使用できます:
app.use('*', serveStatic({ root: './static' }))WARNING
ルートのマッチングは大文字小文字を区別しますが、ファイルシステムはそうではない場合があります。 Windows と macOS では、/ADMIN/secret.txt は /admin/secret.txt と同じファイルとして解決され、Windows は /PROTEC~1 のような 8.3 形式の短縮名も受け付けます。 app.use('/admin/*', auth) のようなパススコープのミドルウェアはそれらの表記にマッチしないため、静的ルート配下のファイルを保護するためにそれらに頼らないでください。 保護するファイルは配信するディレクトリの外に置くか、serveStatic が扱うすべてのパスにミドルウェアを適用してください。
rewriteRequestPath
http://localhost:3000/static/* を ./statics にマップしたい場合、 rewriteRequestPath オプションを使用できます:
app.get(
'/static/*',
serveStatic({
root: './',
rewriteRequestPath: (path) =>
path.replace(/^\/static/, '/statics'),
})
)http2
Hono を Node.js http2 Server でも実行できます。
暗号化されていない http2
import { createServer } from 'node:http2'
const server = serve({
fetch: app.fetch,
createServer,
})暗号化された 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'),
},
})ビルドとデプロイ
npm run buildyarn run buildpnpm run buildbun run buildINFO
フロントエンドのフレームワークをもつアプリケーションは Hono's Vite plugins を使用する必要があるかもしれません。
Dockerfile
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"]