0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 10:51:01 +00:00
hono/docs
Yusuke Wada ac713c0659
refactor: refine directory structure (#156)
* refactor: refine importing mechanism

* refactor: move `CODE_OF_CONDUCT.md`
2022-04-20 14:06:04 +09:00
..
CODE_OF_CONDUCT.md refactor: refine directory structure (#156) 2022-04-20 14:06:04 +09:00
README.ja.md feat: add request method (#132) 2022-03-10 15:44:09 +09:00

Hono[炎]

English · 日本語

GitHub Workflow Status GitHub npm npm npm type definitions GitHub commit activity GitHub last commit

Hono[炎] - 日本語の炎に由来 🔥 - はCloudflare WorkersやFastly Compute@Edge向けの小さくて、シンプルで、めちゃくちゃ速いWebフレームワークです。

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

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

app.fire()

特徴

  • めちゃくちゃ速い - ルーターはぐるぐるループを回しません。
  • 依存ゼロ - Service WorkerもしくはWebスタンダードのAPIしか使っていません。
  • ミドルウェア - ビルトイン・ミドルウェアに加え自分でミドルウェアを作ることができます。
  • 最適化 - Cloudflare Workersに最適化されています。

ベンチマーク

他のCloudflare Workers向けのルーターと比べるとHonoが一番速いです。

hono x 809,503 ops/sec ±6.94% (73 runs sampled)
itty-router x 157,310 ops/sec ±4.31% (87 runs sampled)
sunder x 328,350 ops/sec ±2.30% (95 runs sampled)
worktop x 209,758 ops/sec ±4.28% (83 runs sampled)
Fastest is hono
✨  Done in 60.66s.

1分間で分かるHono

Honoを使って、Cloudflare Workersのアプリケーションを作っている様子です。

Demo

名前付きパラメーターにも型がつきます。

Demo

インストール

NPMリポジトリからインストールできます。

$ yarn add hono

yarn、もしくはnpmコマンドでインストール。

$ npm install hono

メソッド

Honoのインスタンスには以下のメソッドがあります。

  • app.HTTP_METHOD(path, handler)
  • app.all(path, handler)
  • app.route(path)
  • app.use(path, middleware)
  • app.notFound(handler)
  • app.onError(err, handler)
  • app.fire()
  • app.fetch(request, env, event)
  • app.request(path, option)

ルーティング

基本

// HTTPメソッド
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))

// ワイルドカード
app.get('/wild/*/card', (c) => {
  return c.text('GET /wild/*/card')
})

// どんなHTTPメソッドも受け付ける
app.all('/hello', (c) => c.text('Any Method /hello'))

URLからパラメーターを受け取る

app.get('/user/:name', (c) => {
  const name = c.req.param('name')
  ...
})

正規表現

app.get('/post/:date{[0-9]+}/:title{[a-z]+}', (c) => {
  const date = c.req.param('date')
  const title = c.req.param('title')
  ...
})

ネストされたルート

const book = app.route('/book')
book.get('/', (c) => c.text('List Books')) // GET /book と同じ
book.get('/:id', (c) => {
  // GET /book/:id と同じ
  const id = c.req.param('id')
  return c.text('Get Book: ' + id)
})
book.post('/', (c) => c.text('Create Book')) // POST /book と同じ

末尾のスラッシュの扱い

strictがfalseの場合、/hello/hello/は同じように扱われます。

const app = new Hono({ strict: false }) // デフォルトはtrue

app.get('/hello', (c) => c.text('/hello or /hello/'))

async/await

app.get('/fetch-url', async (c) => {
  const response = await fetch('https://example.com/')
  return c.text(`Status is ${response.status}`)
})

ミドルウェア

ビルトイン・ミドルウェア

備え付けのミドルウェアが用意されています。

import { Hono } from 'hono'
import { poweredBy } from 'hono/powered-by'
import { logger } from 'hono/logger'
import { basicAuth } from 'hono/basicAuth'

const app = new Hono()

app.use('*', poweredBy())
app.use('*', logger())
app.use(
  '/auth/*',
  basicAuth({
    username: 'hono',
    password: 'acoolproject',
  })
)

利用可能なビルトイン・ミドルウェアについてはsrc/middlewareを参照してください。

カスタム・ミドルウェア

自分でミドルウェアを書くことができます。

// カスタムロガー
app.use('*', async (c, next) => {
  console.log(`[${c.req.method}] ${c.req.url}`)
  await next()
})

// カスタムヘッダーの追加
app.use('/message/*', async (c, next) => {
  await next()
  c.header('x-message', 'This is middleware!')
})

app.get('/message/hello', (c) => c.text('Hello Middleware!'))

Not Found

「Not Found」レスポンスをカスタマイズした時はapp.notFoundを使います。

app.notFound((c) => {
  return c.text('Custom 404 Message', 404)
})

エラーハンドリング

エラーハンドリングにはapp.onErrorを使います。

app.onError((err, c) => {
  console.error(`${err}`)
  return c.text('Custom Error Message', 500)
})

Context

レスポンス、リクエストを扱うにはContextを使います。

c.req

// リクエストオブジェクトへアクセスする
app.get('/hello', (c) => {
  const userAgent = c.req.headers.get('User-Agent')
  ...
})

// ヘッダーの値を取得する
app.get('/shortcut', (c) => {
  const userAgent = c.req.header('User-Agent')
  ...
})

// クエリーパラメーター
app.get('/search', (c) => {
  const query = c.req.query('q')
  ...
})

// URLからのパラメーターを受け取る
app.get('/entry/:id', (c) => {
  const id = c.req.param('id')
  ...
})

レスポンスへのショートカット

app.get('/welcome', (c) => {
  // ヘッダーの設定
  c.header('X-Message', 'Hello!')
  c.header('Content-Type', 'text/plain')
  // HTTPステータスコードの設定
  c.status(201)
  // レスポンスを返す
  return c.body('Thank you for comming')
})

上記で返すレスポンスは以下と同じです。

new Response('Thank you for comming', {
  status: 201,
  statusText: 'Created',
  headers: {
    'X-Message': 'Hello',
    'Content-Type': 'text/plain',
    'Content-Length': '22',
  },
})

c.text()

Content-Type:text/plainヘッダーをつけてテキストを返します。

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

c.json()

Content-Type:application/jsonヘッダーをつけてJSONを返します。

app.get('/api', (c) => {
  return c.json({ message: 'Hello!' })
})

c.html()

Content-Type:text/htmlヘッダーをつけてHTMLを返します。

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

c.notFound()

Not Foundレスポンスを返します。

app.get('/notfound', (c) => {
  return c.notFound()
})

c.redirect()

リダイレクトします。デフォルトのステータスコードは302です。

app.get('/redirect', (c) => c.redirect('/'))
app.get('/redirect-permanently', (c) => c.redirect('/', 301))

c.res

// レスポンスオブジェクトを取得
app.use('/', (c, next) => {
  next()
  c.res.headers.append('X-Debug', 'Debug message')
})

c.event

// FetchEventオブジェクトを取得
app.use('*', async (c, next) => {
  c.event.waitUntil(
    ...
  )
  await next()
})

c.env

// Environmentオブジェクトへのアクセス。Cloudflare Workers向けです。
app.get('*', async c => {
  const counter = c.env.COUNTER
  ...
})

fire

app.fire()は以下を実行します。

addEventListener('fetch', (event) => {
  event.respondWith(this.handleEvent(event))
})

fetch

app.fetchはCloudflare Module Workerシンタックス向けのメソッドです。

export default {
  fetch(request: Request, env: Env, event: FetchEvent) {
    return app.fetch(request, env, event)
  },
}

/*
もしくは、これでもOK。
export default app
*/

request

requestはテストの時に役に立つメソッドです。

test('GET /hello is ok', async () => {
  const res = await app.request('http://localhost/hello')
  expect(res.status).toBe(200)
})

HonoでCloudflare Workersのアプリを作る

WranglerもしくはMiniflareを使えば、ローカル環境での開発から、デプロイ・公開までが数行のコマンドで簡単にできます。

Honoを使って、Cloudflare Workersのアプリケーションを書いてみましょう。


注意

Wrangler 1.x系 はミドルウェアのインポートに対応していません。2つの方法を推奨します。

  1. Wragler 2.0 Betaを使う。
  2. webpack 4.x系を使わない。例えばesbuildを利用できます。スターターテンプレートを参考にしてみてください。

1. npm init

まず、雛形となるプロジェクトを作成します。

$ mkdir hono-example
$ cd hono-example
$ npm init -y

2. wrangler init

Wrangler向けに初期化します。

$ npx wrangler@beta init

質問されるのでynで答えます。最初、分からないうちはnで構いません。

Would you like to install wrangler into your package.json? (y/n) <--- n
Would you like to use TypeScript? (y/n) <--- n
Would you like to create a Worker at src/index.js? (y/n) <--- n

3. npm install hono

honoをNPMレジストリからインストールします。

$ npm i hono

4. コードを書く

たった4行書くだけです

// index.js
import { Hono } from 'hono'
const app = new Hono()

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

app.fire()

5. 起動させる

ローカルで開発サーバーを立ち上げます。 その後、http://127.0.0.1:8787/にブラウザでアクセスしてみましょう。

$ npx wrangler@beta dev index.js

6. 公開

以下のコマンドでCloudflareにデプロイします。 これで終わりです!

$ npx wrangler@beta publish index.js

スターターテンプレート

Cloudflare Workersのアプリケーションを書き始めるのにスターターテンプレートを使うことができます。 TypeScript、esbuild、Miniflareを使った最小限のものとなっています。

このテンプレートを使った雛形を生成するには、以下のコマンドを打ちます。

$ wrangler generate my-app https://github.com/yusukebe/hono-minimal

関連プロジェクト

最初に作ったHonoのTrieRouterというルーターはgoblinを参考にしました。RegExpRouterRouter::Boomにインスパイアされています。APIのデザインはexpresskoaを参考にしました。同じCloudflare Workersのルーターもしくはフレームワークにはitty-routerSunderworktopがあります。

コントリビュート

コントリビュート歓迎です。以下の方法で貢献できるでしょう。

  • ドキュメントを書いたり、修正する。
  • ミドルウェアのコードを書く。
  • バグフィックス
  • コードのリファクタリング
  • などなど

一緒にHonoを作りましょう

コントリビューターの方々

全てのコントリビューターへ。ありがとう!

作者

Yusuke Wada https://github.com/yusukebe

ライセンス

HonoはMITライセンスのもと開発・公開されています。詳しくはLICENSEをご覧ください。