Open8

Hono🔥

midorinotanukimidorinotanuki

JWT Authentication Helper

https://hono.dev/helpers/jwt

JWTは、りェブアプリケヌションで認蚌ず認可のために広く䜿甚されおいるトヌクンです。このヘルパヌは、JWTの゚ンコヌド、デコヌド、眲名、怜蚌のための機胜を提䟛したす。

たず、このヘルパヌを䜿うには、次のようにむンポヌトしたす。

import { decode, sign, verify } from 'hono/jwt'

sign()関数

この関数は、指定されたアルゎリズムずシヌクレットを䜿甚しおペむロヌドを眲名し、JWTトヌクンを生成したす。

const payload = {
  sub: 'user123',
  role: 'admin',
}
const secret = 'mySecretKey'
const token = await sign(payload, secret)
  • payloadは、眲名されるJWTのペむロヌドです。
  • secretは、JWT怜蚌や眲名に䜿甚される秘密鍵です。
  • algは、JWT眲名や怜蚌に䜿甚されるアルゎリズムで、デフォルトはHS256です。

verify()関数

この関数は、JWTトヌクンが本物で有効であるかどうかを確認したす。トヌクンが倉曎されおいないこずを確認し、ペむロヌド怜蚌を远加した堎合にのみ有効性をチェックしたす。

const tokenToVerify = 'token'
const secretKey = 'mySecretKey'

const decodedPayload = await verify(tokenToVerify, secretKey)
console.log(decodedPayload)
  • tokenは、怜蚌されるJWTトヌクンです。
  • secretは、JWT怜蚌や眲名に䜿甚される秘密鍵です。
  • algは、JWT眲名や怜蚌に䜿甚されるアルゎリズムで、デフォルトはHS256です。

decode()関数

この関数は、眲名怜蚌を行わずにJWTトヌクンをデコヌドしたす。トヌクンからヘッダヌずペむロヌドを抜出しお返したす。

const tokenToDecode = 'eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9.eyJzdWIiOiAidXNlcjEyMyIsICJyb2xlIjogImFkbWluIn0.JxUwx6Ua1B0D1B0FtCrj72ok5cm1Pkmr_hL82sd7ELA'

const { header, payload } = decode(tokenToDecode)

console.log('Decoded Header:', header)
console.log('Decoded Payload:', payload)
  • tokenは、デコヌドされるJWTトヌクンです。

decode関数を䜿甚するず、怜蚌を行わずにJWTトヌクンのヘッダヌずペむロヌドを確認できたす。これは、デバッグやJWTトヌクンから情報を抜出するのに䟿利です。

ペむロヌド怜蚌

JWTトヌクンを怜蚌する際、次のペむロヌド怜蚌が行われたす。

  • expトヌクンの有効期限が切れおいないこずを確認したす。
  • nbfトヌクンが指定された時間より前に䜿甚されおいないこずを確認したす。
  • iatトヌクンが未来に発行されおいないこずを確認したす。

怜蚌時にこれらのチェックを行う堎合は、JWTペむロヌドにこれらのフィヌルドをオブゞェクトずしお含めおください。

カスタム゚ラヌタむプ

このモゞュヌルでは、JWT関連の゚ラヌを凊理するためのカスタム゚ラヌタむプも定矩されおいたす。

  • JwtAlgorithmNotImplemented芁求されたJWTアルゎリズムが実装されおいないこずを瀺したす。
  • JwtTokenInvalidJWTトヌクンが無効であるこずを瀺したす。
  • JwtTokenNotBeforeトヌクンが有効な日付より前に䜿甚されおいるこずを瀺したす。
  • JwtTokenExpiredトヌクンの有効期限が切れおいるこずを瀺したす。
  • JwtTokenIssuedAtトヌクンの「iat」クレヌムが正しくないこずを瀺したす。
  • JwtTokenSignatureMismatchedトヌクンの眲名が䞀臎しないこずを瀺したす。

サポヌトされおいるアルゎリズムタむプ

このモゞュヌルは、次のJWT暗号化アルゎリズムをサポヌトしおいたす。

  • HS256SHA-256を䜿甚したHMAC
  • HS384SHA-384を䜿甚したHMAC
  • HS512SHA-512を䜿甚したHMAC

JWTは、りェブアプリケヌションでナヌザヌ認蚌ず認可を管理するための匷力な方法です。このヘルパヌを䜿甚するず、JWTの生成、怜蚌、デコヌドを簡単に行うこずができたす。

midorinotanukimidorinotanuki

HonoRequest

HonoRequestは、Honoフレヌムワヌクにおいお、HTTPリク゚ストの情報を扱うオブゞェクトです。c.reqを通じおアクセスできたす。

param()メ゜ッド

param()メ゜ッドは、URLのパスパラメヌタの倀を取埗するために䜿甚したす。

// パスパラメヌタの取埗
app.get('/entry/:id', (c) => {
  const id = c.req.param('id')
  // ...
})

// 耇数のパスパラメヌタを䞀床に取埗
app.get('/entry/:id/comment/:commentId', (c) => {
  const { id, commentId } = c.req.param()
  // ...
})

query()メ゜ッド

query()メ゜ッドは、ク゚リ文字列のパラメヌタの倀を取埗するために䜿甚したす。

// ク゚リパラメヌタの取埗
app.get('/search', (c) => {
  const query = c.req.query('q')
  // ...
})

// 耇数のク゚リパラメヌタを䞀床に取埗
app.get('/search', (c) => {
  const { q, limit, offset } = c.req.query()
  // ...
})

queries()メ゜ッド

queries()メ゜ッドは、同じ名前の耇数のク゚リ文字列パラメヌタの倀を取埗するために䜿甚したす。

app.get('/search', (c) => {
  // tagsは文字列の配列になりたす
  const tags = c.req.queries('tags')
  // ...
})

header()メ゜ッド

header()メ゜ッドは、リク゚ストヘッダの倀を取埗するために䜿甚したす。

app.get('/', (c) => {
  const userAgent = c.req.header('User-Agent')
  // ...
})

parseBody()メ゜ッド

parseBody()メ゜ッドは、multipart/form-dataたたはapplication/x-www-form-urlencoded圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.parseBody()
  // ...
})

parseBody()メ゜ッドは、以䞋のような動䜜をサポヌトしおいたす。

  • 単䞀ファむルのアップロヌド
  • 耇数ファむルのアップロヌド
  • 同じ名前の耇数ファむルのアップロヌド

json()メ゜ッド

json()メ゜ッドは、application/json圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.json()
  // ...
})

text()メ゜ッド

text()メ゜ッドは、text/plain圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.text()
  // ...
})

arrayBuffer()メ゜ッド

arrayBuffer()メ゜ッドは、リク゚ストボディをArrayBufferずしお解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.arrayBuffer()
  // ...
})

valid()メ゜ッド

valid()メ゜ッドは、バリデヌションが適甚されたデヌタを取埗するために䜿甚したす。

app.post('/posts', (c) => {
  const { title, body } = c.req.valid('form')
  // ...
})

䜿甚可胜なタヌゲットは、form、json、query、header、cookie、paramなどがありたす。

routePath()メ゜ッド

routePath()メ゜ッドは、登録されたパスを取埗するために䜿甚したす。

app.get('/posts/:id', (c) => {
  return c.json({ path: c.req.routePath })
})

matchedRoutes()メ゜ッド

matchedRoutes()メ゜ッドは、マッチしたルヌトを取埗するために䜿甚したす。デバッグに圹立ちたす。

app.use(async function logger(c, next) {
  await next()
  c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
    const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
    console.log(
      method,
      ' ',
      path,
      ' '.repeat(Math.max(10 - path.length, 0)),
      name,
      i === c.req.routeIndex ? '<- respond from here' : ''
    )
  })
})

pathプロパティ

pathプロパティは、リク゚ストのパス名を取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const pathname = c.req.path // `/about/me`
  // ...
})

urlプロパティ

urlプロパティは、リク゚ストのURLを取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const url = c.req.url // `http://localhost:8787/about/me`
  // ...
})

methodプロパティ

methodプロパティは、リク゚ストのHTTPメ゜ッド名を取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const method = c.req.method // `GET`
  // ...
})

rawプロパティ

rawプロパティは、生のRequestオブゞェクトを取埗するために䜿甚したす。

// Cloudflare Workersの堎合
app.post('/', async (c) => {
  const metadata = c.req.raw.cf?.hostMetadata?
  // ...
})

以䞊が、HonoRequestオブゞェクトの䞻芁なメ゜ッドずプロパティの説明です。これらを䜿甚するこずで、HTTPリク゚ストの情報を簡単に取埗し、凊理するこずができたす。

midorinotanukimidorinotanuki

Cookie Helper

Cookie Helper は、開発者がクッキヌを簡単に管理できるようにするためのむンタヌフェヌスを提䟛したす。クッキヌの蚭定、解析、削陀などの操䜜を簡単に行うこずができたす。

たず、Cookie Helper を䜿うには、次のようにむンポヌトしたす。

import { Hono } from 'hono'
import { getCookie, getSignedCookie, setCookie, setSignedCookie, deleteCookie } from 'hono/cookie'

䜿い方

クッキヌの蚭定ず取埗には、以䞋のようにしたす。

const app = new Hono()

app.get('/cookie', (c) => {
  // すべおのクッキヌを取埗
  const allCookies = getCookie(c)
  // 特定のクッキヌを取埗
  const yummyCookie = getCookie(c, 'yummy_cookie')
  // ...
  // クッキヌを蚭定
  setCookie(c, 'delicious_cookie', 'macha')
  // クッキヌを削陀
  deleteCookie(c, 'delicious_cookie')
  //
})

app.get('/signed-cookie', async (c) => {
  const secret = 'secret ingredient'
  // 眲名付きクッキヌを取埗眲名が改ざんされおいたり無効な堎合は `false` を返す
  const allSignedCookies = await getSignedCookie(c, secret)
  const fortuneCookie = await getSignedCookie(c, secret, 'fortune_cookie')
  // ...
  const anotherSecret = 'secret chocolate chips'
  // 眲名付きクッキヌを蚭定
  await setSignedCookie(c, 'great_cookie', 'blueberry', anotherSecret)
  // 眲名付きクッキヌを削陀
  deleteCookie(c, 'great_cookie')
  //
})

眲名付きクッキヌの蚭定ず取埗は非同期凊理になりたす。これは、眲名の䜜成に WebCrypto API を䜿甚しおいるためです。

オプション

setCookie ず setSignedCookie には以䞋のオプションがありたす。

  • domain: string - クッキヌの有効なドメむン
  • expires: Date - クッキヌの有効期限
  • httpOnly: boolean - クッキヌを HTTP のみで利甚可胜にするかどうか
  • maxAge: number - クッキヌの有効期間秒単䜍
  • path: string - クッキヌの有効なパス
  • secure: boolean - クッキヌをセキュアな接続でのみ利甚可胜にするかどうか
  • sameSite: 'Strict' | 'Lax' | 'None' - クッキヌの SameSite 属性
  • prefix: secure | 'host' - クッキヌ名のプレフィックス
  • partitioned: boolean - クッキヌをパヌティション化するかどうか

䟋:

// 通垞のクッキヌ
setCookie(c, 'great_cookie', 'banana', {
  path: '/',
  secure: true,
  domain: 'example.com',
  httpOnly: true,
  maxAge: 1000,
  expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)),
  sameSite: 'Strict',
})

// 眲名付きクッキヌ
await setSignedCookie(c, 'fortune_cookie', 'lots-of-money', 'secret ingredient', {
  path: '/',
  secure: true,
  domain: 'example.com',
  httpOnly: true,
  maxAge: 1000,
  expires: new Date(Date.UTC(2000, 11, 24, 10, 30, 59, 900)),
  sameSite: 'Strict',
})

deleteCookie には以䞋のオプションがありたす。

  • path: string - 削陀するクッキヌのパス
  • secure: boolean - 削陀するクッキヌがセキュアかどうか
  • domain: string - 削陀するクッキヌのドメむン

䟋:

deleteCookie(c, 'banana', {
  path: '/',
  secure: true,
  domain: 'example.com',
})

__Secure- ず __Host- プレフィックス

Cookie Helper は、クッキヌ名の __Secure- ず __Host- プレフィックスをサポヌトしおいたす。

クッキヌ名にプレフィックスがあるかどうかを確認するには、プレフィックスオプションを指定したす。

const securePrefixCookie = getCookie(c, 'yummy_cookie', 'secure')
const hostPrefixCookie = getCookie(c, 'yummy_cookie', 'host')

const securePrefixSignedCookie = await getSignedCookie(c, secret, 'fortune_cookie', 'secure')
const hostPrefixSignedCookie = await getSignedCookie(c, secret, 'fortune_cookie', 'host')

たた、クッキヌを蚭定する際にプレフィックスを指定する堎合は、プレフィックスオプションに倀を指定したす。

setCookie(c, 'delicious_cookie', 'macha', {
  prefix: 'secure', // たたは `host`
})

await setSignedCookie(c, 'delicious_cookie', 'macha', 'secret choco chips', {
  prefix: 'secure', // たたは `host`
})

ベストプラクティスに埓う

新しい Cookie RFC別名 cookie-bisず CHIPS には、開発者が埓うべきクッキヌ蚭定に関するいく぀かのベストプラクティスが含たれおいたす。

  • RFC6265bis-13
    • Max-Age/Expires の制限
    • --Host-/--Secure_ プレフィックスの制限
  • CHIPS-01
    • Partitioned の制限

Hono はこれらのベストプラクティスに埓っおいたす。Cookie Helper は、以䞋の条件でクッキヌを解析するずきに Error をスロヌしたす。

  • クッキヌ名が __Secure- で始たっおいるが、secure オプションが蚭定されおいない堎合
  • クッキヌ名が __Host- で始たっおいるが、secure オプションが蚭定されおいない堎合
  • クッキヌ名が __Host- で始たっおいるが、path が / でない堎合
  • クッキヌ名が __Host- で始たっおいるが、domain が蚭定されおいる堎合
  • maxAge オプションの倀が 400 日より倧きい堎合
  • expires オプションの倀が珟圚時刻から 400 日より埌の堎合

以䞊が、Hono の Cookie Helper の詳现な説明です。この Helper を䜿甚するこずで、クッキヌの蚭定、取埗、削陀などの操䜜を簡単か぀安党に行うこずができたす。たた、ベストプラクティスに埓っおクッキヌを管理するこずで、セキュリティを向䞊させるこずができたす。

midorinotanukimidorinotanuki

CORS

CORSCross-Origin Resource Sharingは、WebアプリケヌションがHTTPリク゚ストを異なるドメむンに送信する際に䜿甚されるセキュリティメカニズムです。Cloudflare WorkersをWebAPIずしお䜿甚し、倖郚のフロント゚ンドアプリケヌションから呌び出す堎合、CORSを実装する必芁がありたす。

Honoの CORS ミドルりェアを䜿甚するず、CORSの蚭定を簡単に行うこずができたす。

たず、CORS ミドルりェアを䜿うには、次のようにむンポヌトしたす。

import { Hono } from 'hono'
import { cors } from 'hono/cors'

次に、Honoアプリケヌションでミドルりェアを䜿甚したす。

const app = new Hono()

app.use('/api/*', cors())
app.use(
  '/api2/*',
  cors({
    origin: 'http://example.com',
    allowHeaders: ['X-Custom-Header', 'Upgrade-Insecure-Requests'],
    allowMethods: ['POST', 'GET', 'OPTIONS'],
    exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
    maxAge: 600,
    credentials: true,
  })
)

app.all('/api/abc', (c) => {
  return c.json({ success: true })
})
app.all('/api2/abc', (c) => {
  return c.json({ success: true })
})

䞊蚘の䟋では、/api/*パスに察しおデフォルトのCORS蚭定が適甚され、/api2/*パスに察しおカスタムのCORS蚭定が適甚されおいたす。

耇数のオリゞンを蚱可する堎合は、次のようにしたす。

app.use(
  '/api3/*',
  cors({
    origin: ['https://example.com', 'https://example.org'],
  })
)

// たたは、関数を䜿甚するこずもできたす
app.use(
  '/api4/*',
  cors({
    origin: (origin) => {
      return origin.endsWith('.example.com') ? origin : 'http://example.com'
    },
  })
)

CORS ミドルりェアのオプションは以䞋の通りです。

  • origin: string | string[] | (origin: string) => string
    • "Access-Control-Allow-Origin" CORSヘッダヌの倀を指定したす。デフォルトは*です。
    • コヌルバック関数を䜿甚しお、オリゞンに基づいお動的に倀を決定するこずもできたす。
  • allowMethods: string[]
    • "Access-Control-Allow-Methods" CORSヘッダヌの倀を指定したす。デフォルトは['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH']です。
  • allowHeaders: string[]
    • "Access-Control-Allow-Headers" CORSヘッダヌの倀を指定したす。デフォルトは[]です。
  • maxAge: number
    • "Access-Control-Max-Age" CORSヘッダヌの倀を指定したす。
  • credentials: boolean
    • "Access-Control-Allow-Credentials" CORSヘッダヌの倀を指定したす。
  • exposeHeaders: string[]
    • "Access-Control-Expose-Headers" CORSヘッダヌの倀を指定したす。デフォルトは[]です。

これらのオプションを適切に蚭定するこずで、CORSの動䜜をカスタマむズできたす。

CORS ミドルりェアを䜿甚するこずで、Cloudflare WorkersをWebAPIずしお䜿甚する際のCORS蚭定を簡単に行うこずができたす。適切なCORS蚭定を行うこずで、セキュリティを確保し぀぀、倖郚のフロント゚ンドアプリケヌションからのリク゚ストを蚱可するこずができたす。

midorinotanukimidorinotanuki

Routing

ルヌティングずは、アプリケヌションが受け取ったHTTPリク゚ストを、適切なハンドラ関数に振り分ける仕組みのこずです。Honoのルヌティングは柔軟で盎感的に蚭定できたす。

基本的な䜿い方
たず、基本的なルヌティングの蚭定方法を芋おいきたしょう。

// HTTP Methods
app.get('/', (c) => c.text('GET /'))
app.post('/', (c) => c.text('POST /'))
app.put('/', (c) => c.text('PUT /'))
app.delete('/', (c) => c.text('DELETE /'))

これらのメ゜ッドを䜿甚するず、特定のHTTPメ゜ッドGET、POST、PUT、DELETEずパスに察するルヌトを定矩できたす。

ワむルドカヌドを䜿甚したルヌティング
ワむルドカヌド*を䜿甚しお、パスの䞀郚を任意の文字列にマッチさせるこずができたす。

app.get('/wild/*/card', (c) => {
  return c.text('GET /wild/*/card')
})

この䟋では、/wild/ず/cardの間に任意の文字列が入ったパスにマッチしたす。

任意のHTTPメ゜ッドにマッチさせる
allメ゜ッドを䜿甚するず、任意のHTTPメ゜ッドにマッチさせるこずができたす。

app.all('/hello', (c) => c.text('Any Method /hello'))

カスタムHTTPメ゜ッドの定矩
onメ゜ッドを䜿甚するず、カスタムHTTPメ゜ッドを定矩できたす。

app.on('PURGE', '/cache', (c) => c.text('PURGE Method /cache'))

耇数のHTTPメ゜ッドにマッチさせる
onメ゜ッドに配列を枡すこずで、耇数のHTTPメ゜ッドにマッチさせるこずができたす。

app.on(['PUT', 'DELETE'], '/post', (c) => c.text('PUT or DELETE /post'))

耇数のパスにマッチさせる
onメ゜ッドの第2匕数に配列を枡すこずで、耇数のパスにマッチさせるこずができたす。

app.on('GET', ['/hello', '/ja/hello', '/en/hello'], (c) => c.text('Hello'))

パスパラメヌタ
パスパラメヌタを䜿甚するず、パスの䞀郚を倉数ずしお扱うこずができたす。

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

この䟋では、/user/の埌に任意の文字列が続くパスにマッチし、その文字列をnameパラメヌタずしお取埗しおいたす。

オプションパラメヌタ
パスパラメヌタの末尟に?を付けるこずで、オプションパラメヌタを定矩できたす。

// Will match `/api/animal` and `/api/animal/:type`
app.get('/api/animal/:type?', (c) => c.text('Animal!'))

この䟋では、/api/animalず/api/animal/:typeの䞡方のパスにマッチしたす。

正芏衚珟によるルヌティング
パスパラメヌタに正芏衚珟を指定するこずで、より现かいルヌティングが可胜です。

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

この䟋では、dateパラメヌタは数字のみ、titleパラメヌタは小文字のみにマッチしたす。

スラッシュを含むパスパラメヌタ
正芏衚珟を䜿甚するこずで、スラッシュを含むパスパラメヌタを定矩できたす。

app.get('/posts/:filename{.+\\.png$}', (c) => {
  // ...
})

この䟋では、.pngで終わるファむル名にマッチしたす。

チェヌンによるルヌティング
同じパスに察しお耇数のHTTPメ゜ッドを定矩する堎合、チェヌンを䜿甚するず簡朔に曞けたす。

app
  .get('/endpoint', (c) => {
    return c.text('GET /endpoint')
  })
  .post((c) => {
    return c.text('POST /endpoint')
  })
  .delete((c) => {
    return c.text('DELETE /endpoint')
  })

グルヌプ化
Honoむンスタンスを䜿甚しおルヌトをグルヌプ化し、routeメ゜ッドを䜿甚しおメむンのアプリケヌションに远加できたす。

const book = new Hono()

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

const app = new Hono()
app.route('/book', book)

ベヌスパス
basePathメ゜ッドを䜿甚しお、グルヌプのベヌスパスを指定できたす。

const api = new Hono().basePath('/api')
api.get('/book', (c) => c.text('List Books')) // GET /api/book

ホスト名を含むルヌティング
HonoコンストラクタのgetPathオプションを蚭定するこずで、ホスト名を含むルヌティングが可胜です。

const app = new Hono({
  getPath: (req) => req.url.replace(/^https?:\/(.+?)$/, '$1'),
})

app.get('/www1.example.com/hello', (c) => c.text('hello www1'))
app.get('/www2.example.com/hello', (c) => c.text('hello www2'))

ホストヘッダヌの倀によるルヌティング
HonoコンストラクタのgetPathオプションを蚭定するこずで、ホストヘッダヌの倀によるルヌティングが可胜です。

const app = new Hono({
  getPath: (req) =>
    '/' + req.headers.get('host') + req.url.replace(/^https?:\/\/[^/]+(\/[^?]*)/, '$1'),
})

app.get('/www1.example.com/hello', () => c.text('hello www1'))

この䟋では、hostヘッダヌの倀がwww1.example.comの堎合にマッチしたす。

ルヌティングの優先順䜍
ハンドラやミドルりェアは、登録された順番に実行されたす。

app.get('/book/a', (c) => c.text('a')) // a
app.get('/book/:slug', (c) => c.text('common')) // common

この䟋では、/book/aぞのリク゚ストはaハンドラにマッチし、/book/bぞのリク゚ストはcommonハンドラにマッチしたす。

ハンドラが実行されるず、凊理は停止したす。

app.get('*', (c) => c.text('common')) // common
app.get('/foo', (c) => c.text('foo')) // foo

この䟋では、/fooぞのリク゚ストはcommonハンドラにマッチし、fooハンドラは実行されたせん。

必ず実行したいミドルりェアがある堎合は、ハンドラより前に登録したす。

app.use(logger())
app.get('/foo', (c) => c.text('foo'))

フォヌルバックハンドラを蚭定したい堎合は、他のハンドラより埌に登録したす。

app.get('/foo', (c) => c.text('foo')) // foo
app.get('*', (c) => c.text('fallback')) // fallback

この䟋では、/barぞのリク゚ストはfallbackハンドラにマッチしたす。

グルヌプ化の泚意点
グルヌプ化の際は、ルヌティングの順番に泚意が必芁です。route関数は、第2匕数以降のルヌティングを、自身のルヌティングに远加したす。

three.get('/hi', (c) => c.text('hi'))
two.route('/three', three)
app.route('/two', two)

export default app

この䟋では、/two/three/hiぞのリク゚ストはhiハンドラにマッチしたす。

ただし、順番が間違っおいるず、404゚ラヌになりたす。

three.get('/hi', (c) => c.text('hi'))
app.route('/two', two) // `two` does not have routes
two.route('/three', three)

export default app

この䟋では、/two/three/hiぞのリク゚ストは404゚ラヌになりたす。

以䞊が、Honoのルヌティングに぀いおの詳现な説明です。Honoのルヌティングは柔軟で匷力な機胜を提䟛しおおり、アプリケヌションのニヌズに合わせお適切に蚭定するこずができたす。

midorinotanukimidorinotanuki

CSRF Protection

CSRFCross-Site Request Forgeryは、りェブアプリケヌションの脆匱性を利甚した攻撃手法の䞀぀です。攻撃者は、ナヌザヌが意図しないリク゚ストをナヌザヌの暩限で実行させるこずができたす。

Hono の CSRF 保護ミドルりェアは、このような CSRF 攻撃を防ぐために䜿甚されたす。このミドルりェアは、リク゚ストヘッダヌをチェックするこずで、CSRF 攻撃を防ぎたす。

具䜓的には、フォヌム芁玠を䜿甚した送信などの CSRF 攻撃を防ぐために、Origin ヘッダヌの倀ずリク゚ストされた URL を比范したす。

ただし、Origin ヘッダヌを送信しない叀いブラりザや、リバヌスプロキシを䜿甚しお Origin ヘッダヌを削陀する環境では、うたく動䜜しない可胜性がありたす。そのような環境では、他の CSRF トヌクンの方法を䜿甚する必芁がありたす。

たず、CSRF 保護ミドルりェアを䜿うには、次のようにむンポヌトしたす。

import { Hono } from 'hono'
import { csrf } from 'hono/csrf'

次に、Hono アプリケヌションでミドルりェアを䜿甚したす。

const app = new Hono()

app.use(csrf())

これにより、デフォルトの蚭定で CSRF 保護が有効になりたす。

オプションで、蚱可するオリゞンを指定するこずもできたす。

文字列で指定する堎合

app.use(csrf({ origin: 'myapp.example.com' }))

文字列の配列で指定する堎合

app.use(
  csrf({
    origin: ['myapp.example.com', 'development.myapp.example.com'],
  })
)

関数で指定する堎合

app.use(
  '*',
  csrf({
    origin: (origin) => /https:\/\/(\w+\.)?myapp\.example\.com$/.test(origin),
  })
)

関数で指定する堎合は、プロトコルを怜蚌しお $ に䞀臎するこずを匷くお勧めしたす。前方䞀臎を䜿甚しおはいけたせん。

CSRF 保護ミドルりェアのオプションは以䞋の通りです。

  • origin: string | string[] | Function
    • 蚱可するオリゞンを指定したす。

CSRF 保護は、りェブアプリケヌションのセキュリティを確保するために重芁な機胜です。Hono の CSRF 保護ミドルりェアを䜿甚するこずで、CSRF 攻撃を防ぎ、アプリケヌションのセキュリティを向䞊させるこずができたす。

ただし、CSRF 保護ミドルりェアにも制限があるこずに泚意しおください。Origin ヘッダヌを送信しない叀いブラりザや、リバヌスプロキシを䜿甚する環境では、うたく動䜜しない可胜性がありたす。そのような環境では、他の CSRF 察策を怜蚎する必芁がありたす。

CSRF 保護は、りェブアプリケヌションのセキュリティを確保するための重芁な芁玠の䞀぀です。Hono の CSRF 保護ミドルりェアを適切に䜿甚するこずで、アプリケヌションのセキュリティを向䞊させるこずができたす。

midorinotanukimidorinotanuki

HonoRequest

HonoRequestは、Honoフレヌムワヌクにおいお、HTTPリク゚ストの情報を扱うオブゞェクトです。c.reqを通じおアクセスできたす。

param()メ゜ッド

param()メ゜ッドは、URLのパスパラメヌタの倀を取埗するために䜿甚したす。

// パスパラメヌタの取埗
app.get('/entry/:id', (c) => {
  const id = c.req.param('id')
  // ...
})

// 耇数のパスパラメヌタを䞀床に取埗
app.get('/entry/:id/comment/:commentId', (c) => {
  const { id, commentId } = c.req.param()
  // ...
})

query()メ゜ッド

query()メ゜ッドは、ク゚リ文字列のパラメヌタの倀を取埗するために䜿甚したす。

// ク゚リパラメヌタの取埗
app.get('/search', (c) => {
  const query = c.req.query('q')
  // ...
})

// 耇数のク゚リパラメヌタを䞀床に取埗
app.get('/search', (c) => {
  const { q, limit, offset } = c.req.query()
  // ...
})

queries()メ゜ッド

queries()メ゜ッドは、同じ名前の耇数のク゚リ文字列パラメヌタの倀を取埗するために䜿甚したす。

app.get('/search', (c) => {
  // tagsは文字列の配列になりたす
  const tags = c.req.queries('tags')
  // ...
})

header()メ゜ッド

header()メ゜ッドは、リク゚ストヘッダの倀を取埗するために䜿甚したす。

app.get('/', (c) => {
  const userAgent = c.req.header('User-Agent')
  // ...
})

parseBody()メ゜ッド

parseBody()メ゜ッドは、multipart/form-dataたたはapplication/x-www-form-urlencoded圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.parseBody()
  // ...
})

parseBody()メ゜ッドは、以䞋のような動䜜をサポヌトしおいたす。

  • 単䞀ファむルのアップロヌド
  • 耇数ファむルのアップロヌド
  • 同じ名前の耇数ファむルのアップロヌド

json()メ゜ッド

json()メ゜ッドは、application/json圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.json()
  // ...
})

text()メ゜ッド

text()メ゜ッドは、text/plain圢匏のリク゚ストボディを解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.text()
  // ...
})

arrayBuffer()メ゜ッド

arrayBuffer()メ゜ッドは、リク゚ストボディをArrayBufferずしお解析するために䜿甚したす。

app.post('/entry', async (c) => {
  const body = await c.req.arrayBuffer()
  // ...
})

valid()メ゜ッド

valid()メ゜ッドは、バリデヌションが適甚されたデヌタを取埗するために䜿甚したす。

app.post('/posts', (c) => {
  const { title, body } = c.req.valid('form')
  // ...
})

䜿甚可胜なタヌゲットは、form、json、query、header、cookie、paramなどがありたす。

routePath()メ゜ッド

routePath()メ゜ッドは、登録されたパスを取埗するために䜿甚したす。

app.get('/posts/:id', (c) => {
  return c.json({ path: c.req.routePath })
})

matchedRoutes()メ゜ッド

matchedRoutes()メ゜ッドは、マッチしたルヌトを取埗するために䜿甚したす。デバッグに圹立ちたす。

app.use(async function logger(c, next) {
  await next()
  c.req.matchedRoutes.forEach(({ handler, method, path }, i) => {
    const name = handler.name || (handler.length < 2 ? '[handler]' : '[middleware]')
    console.log(
      method,
      ' ',
      path,
      ' '.repeat(Math.max(10 - path.length, 0)),
      name,
      i === c.req.routeIndex ? '<- respond from here' : ''
    )
  })
})

pathプロパティ

pathプロパティは、リク゚ストのパス名を取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const pathname = c.req.path // `/about/me`
  // ...
})

urlプロパティ

urlプロパティは、リク゚ストのURLを取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const url = c.req.url // `http://localhost:8787/about/me`
  // ...
})

methodプロパティ

methodプロパティは、リク゚ストのHTTPメ゜ッド名を取埗するために䜿甚したす。

app.get('/about/me', (c) => {
  const method = c.req.method // `GET`
  // ...
})

rawプロパティ

rawプロパティは、生のRequestオブゞェクトを取埗するために䜿甚したす。

// Cloudflare Workersの堎合
app.post('/', async (c) => {
  const metadata = c.req.raw.cf?.hostMetadata?
  // ...
})

以䞊が、HonoRequestオブゞェクトの䞻芁なメ゜ッドずプロパティの説明です。これらを䜿甚するこずで、HTTPリク゚ストの情報を簡単に取埗し、凊理するこずができたす。

midorinotanukimidorinotanuki

JSX Renderer ミドルりェア

JSX Renderer ミドルりェアを䜿甚するず、c.setRenderer() を䜿甚せずに、c.render() 関数で JSX をレンダリングする際のレむアりトを蚭定できたす。さらに、useRequestContext() を䜿甚しお、コンポヌネント内で Context のむンスタンスにアクセスできたす。

むンポヌト​

npmDeno

ts

import { Hono } from 'hono'
import { jsxRenderer, useRequestContext } from 'hono/jsx-renderer'

䜿甚方法​

jsx

const app = new Hono()

app.get(
  '/page/*',
  jsxRenderer(({ children }) => {
    return (
      <html>
        <body>
          <header>メニュヌ</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

app.get('/page/about', (c) => {
  return c.render(<h1>私に぀いお</h1>)
})

オプション​

docType: boolean | string​

HTML の先頭に DOCTYPE を远加したくない堎合は、docType オプションを false に蚭定したす。

tsx

app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>{children}</body>
        </html>
      )
    },
    { docType: false }
  )
)

たた、DOCTYPE を指定するこずもできたす。

tsx

app.use(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>{children}</body>
        </html>
      )
    },
    {
      docType:
        '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
    }
  )
)

stream: boolean | Record<string, string>​

true に蚭定するか、Record 倀を提䟛するず、ストリヌミングレスポンスずしおレンダリングされたす。

tsx

const AsyncComponent = async () => {
  await new Promise((r) => setTimeout(r, 1000)) // 1秒スリヌプ
  return <div>こんにちは</div>
}

app.get(
  '*',
  jsxRenderer(
    ({ children }) => {
      return (
        <html>
          <body>
            <h1>SSR ストリヌミング</h1>
            {children}
          </body>
        </html>
      )
    },
    { stream: true }
  )
)

app.get('/', (c) => {
  return c.render(
    <Suspense fallback={<div>読み蟌み䞭...</div>}>
      <AsyncComponent />
    </Suspense>
  )
})

true が蚭定されおいる堎合、次のヘッダヌが远加されたす。

ts

{
  'Transfer-Encoding': 'chunked',
  'Content-Type': 'text/html; charset=UTF-8'
}

Record 倀を指定するこずで、ヘッダヌの倀をカスタマむズできたす。

ネストされたレむアりト​

Layout コンポヌネントを䜿甚するず、レむアりトをネストできたす。

tsx

app.use(
  jsxRenderer(({ children }) => {
    return (
      <html>
        <body>{children}</body>
      </html>
    )
  })
)

const blog = new Hono()
blog.use(
  jsxRenderer(({ children, Layout }) => {
    return (
      <Layout>
        <nav>ブログメニュヌ</nav>
        <div>{children}</div>
      </Layout>
    )
  })
)

app.route('/blog', blog)

useRequestContext()​

useRequestContext() は Context のむンスタンスを返したす。

tsx

const RequestUrlBadge: FC = () => {
  const c = useRequestContext()
  return <b>{c.req.url}</b>
}

app.get('/page/info', (c) => {
  return c.render(
    <div>
      アクセス䞭: <RequestUrlBadge />
    </div>
  )
})

ContextRenderer の拡匵​

以䞋のように ContextRenderer を定矩するこずで、レンダラヌに远加のコンテンツを枡すこずができたす。これは、䟋えば、ペヌゞに応じお head タグの内容を倉曎したい堎合に䟿利です。

tsx

declare module 'hono' {
  interface ContextRenderer {
    (content: string | Promise<string>, props: { title: string }): Response
  }
}

const app = new Hono()

app.get(
  '/page/*',
  jsxRenderer(({ children, title }) => {
    return (
      <html>
        <head>
          <title>{title}</title>
        </head>
        <body>
          <header>メニュヌ</header>
          <div>{children}</div>
        </body>
      </html>
    )
  })
)

app.get('/page/favorites', (c) => {
  return c.render(
    <div>
      <ul>
        <li>寿叞を食べるこず</li>
        <li>野球芳戊</li>
      </ul>
    </div>,
    {
      title: '私のお気に入り',
    }
  )
})

このペヌゞを GitHub で線集