本文へスキップ
Typle
ログイン状態を確認中

プログラミング · Hono · TypeScript

Honoメソッドのタイピング練習

Hono(TypeScript)のメソッド36個を、初級・中級・上級の3つのリストで打って覚えます。app.get でのルート定義、c.req.query でのクエリの取得、c.req.parseBody でのフォームの読み取りなどを練習できます。

3リスト・36語

無料・ログインなしで練習できます。押すとこのパックをフォローし、すぐに練習が始まります。

収録している言葉

Honoメソッド(初級)

打つ言葉意味・補足
import { Hono } from 'hono'; const app = new Hono();Hono / TypeScript:対象環境(現行公式APIで共通的な機能を中心に整理。実行環境・メジャー版・adapterを固定して利用)
app.get('/', c => c.text('Hello, Hono!'));GET / c.text:テキスト応答(必ずResponse等をreturn)
app.post('/users', c => c.json({ id: 1 }, 201));c.json / status:JSONとステータス(実際の保存・認証認可・入力検証は別途)
app.get('/page', c => c.html('<h1>Hello</h1>'));c.html / c.body:HTML・本文(c.htmlへ未検証入力を直接連結しない)
app.delete('/items/:id', c => c.body(null, 204));get/post/put/patch/delete:メソッド別ルート(実削除には認可等が必要)
app.get('/users/:id', c => c.json({ id: c.req.param('id') }));c.req.param:パス引数(文字列。型・範囲を検証)
app.get('/old', c => c.redirect('/new', 302));c.redirect:転送(未検証の転送先を使わない)
app.notFound(c => c.json({ error: 'not found' }, 404));c.notFound / app.notFound:404(特定ハンドラーからreturn c.notFound()も可能)
export default app;Workers / Bun等:Fetch入口をexport(ランタイム・デプロイ設定に応じた入口。Nodeはadapter等が必要)
npm install hono @hono/node-server導入・実行:開発コマンド(Node.jsの例。型検査はtsc等で別途)
npm install -D typescript tsx @types/node導入・実行:開発コマンド(Node.jsの例。型検査はtsc等で別途)
npx tsx src/index.ts導入・実行:開発コマンド(Node.jsの例。型検査はtsc等で別途)

Honoメソッド(中級)

打つ言葉意味・補足
app.get('/search', c => c.json({ q: c.req.query('q'), tags: c.req.queries('tag') }));c.req.query / queries:クエリ取得(queryは単一、queriesは複数値)
const users = new Hono(); users.get('/', c => c.json([])); app.route('/users', users);app.route:サブアプリをマウント(子のルート定義・登録順を確認)
const api = new Hono().basePath('/api'); api.get('/health', c => c.json({ ok: true }));basePath:共通プレフィックス(/api/health)
app.post('/echo', async c => { const body: unknown = await c.req.json(); return c.json({ received: body }); });c.req.json:JSON本文(unknownから実行時検証。大きさ制限・不正JSONの処理も用意)
/* validatorまたはzValidatorの後のハンドラー内: */ /* const input=c.req.valid('json'); */c.req.valid:検証済み値を取得(TypeScriptの推論には同じルートのmiddleware連結を保つ)
app.get('/header', c => { c.header('X-App', 'sample'); return c.text('ok') });c.header:応答ヘッダー
import { logger } from 'hono/logger'; import { timing } from 'hono/timing'; app.use('*', logger(), timing());logger / timing:ログ・計測(ログに秘密・個人情報を含めない)
import { cors } from 'hono/cors'; app.use('/api/*', cors({ origin: 'https://app.example.com' }));cors:CORS(認証・認可の代わりではない)
import { secureHeaders } from 'hono/secure-headers'; app.use('*', secureHeaders());secureHeaders:ヘッダー保護(ポリシーをアプリに合わせる)
import { bodyLimit } from 'hono/body-limit'; app.use('/upload', bodyLimit({ maxSize: 1024 * 1024 }));bodyLimit:本文サイズ制限(実行環境の上限も別途確認)
const response = await app.request('/'); if (response.status !== 200) throw new Error('unexpected status');app.request:サーバー起動なしのリクエスト(アプリが定義済み。テストランナーのexpect等とも組合せ可能)
import { serve } from '@hono/node-server'; serve({ fetch: app.fetch, port: 3000 });Node.js adapter:Nodeで起動(補足: @hono/node-serverを別途導入。実行環境に合う版を使用)

Honoメソッド(上級)

打つ言葉意味・補足
app.post('/form', async c => { const body = await c.req.parseBody(); return c.json({ name: typeof body.name === 'string' ? body.name : '' }); });c.req.parseBody / formData:フォーム本文(Content-Type・File等の型を確認)
import { validator } from 'hono/validator'; app.post('/users', validator('json', (value, c) => { if (!value || typeof value.name !== 'string') return c.json({ error: 'invalid name' }, 400); return { name: value.name }; }), c => c.json(c.req.valid('json'), 201));validator:組込みの薄い検証(JSONには適切なContent-Typeが必要)
import { z } from 'zod'; import { zValidator } from '@hono/zod-validator'; app.post( '/users', zValidator('json', z.object({ name: z.string().min(1) })), c => c.json(c.req.valid('json'), 201) );zValidator:Zodで検証(zod・@hono/zod-validatorを別途導入。版の互換性を確認)
app.use('*', async (c, next) => { console.log(c.req.method, c.req.path); await next(); c.header('X-App', 'sample'); });app.use / await next:前後処理(ルートより前に登録。nextをawait)
import { createMiddleware } from 'hono/factory'; const log = createMiddleware(async (c, next) => { console.log(c.req.path); await next(); }); app.use('*', log);createMiddleware:型付きの独立middleware
type Env = { Variables: { requestId: string } }; const traced = new Hono<Env>(); traced.use('*', async (c, next) => { c.set('requestId', crypto.randomUUID()); await next() }); traced.get('/', c => c.json({ id: c.get('requestId') }));c.set / c.get:リクエスト単位の値(対応middlewareを通ることは型宣言だけでは保証されない)
type Env = { Bindings: { APP_NAME: string } }; const worker = new Hono<Env>(); worker.get('/', c => c.text(c.env.APP_NAME));c.env:環境固有のBindings(Cloudflare Workers等でBindingsを設定。Nodeのprocess.envとは同一でない)
import { HTTPException } from 'hono/http-exception'; app.get('/private', () => { throw new HTTPException(401, { message: 'Unauthorized' }) });HTTPException:HTTPエラーを投げる(例は常に拒否。実際の認証を別途実装)
app.onError((err, c) => { if (err instanceof HTTPException) return err.getResponse(); console.error(err); return c.json({ error: 'internal error' }, 500); });app.onError:例外を応答へ変換(HTTPExceptionを別途import。機密情報を返さない)
import { getCookie, setCookie, deleteCookie } from 'hono/cookie'; app.post('/theme', c => { setCookie(c, 'theme', 'dark', { httpOnly: true, sameSite: 'Lax', path: '/' }); return c.json({ ok: true }) });getCookie / setCookie / deleteCookie:Cookie操作(本番HTTPSでsecure等を設定。認証CookieはCSRF・期限も設計)
const routes = app.get('/health', c => c.json({ ok: true })); export type AppType = typeof routes; /* クライアント別ファイル: */ /* import {hc} from 'hono/client'; */ /* const client=hc<AppType>('http://localhost:3000'); */hc / AppType:サーバーとクライアントの型共有(ルートを連結して推論を保持。型共有は認証や実行時検証を代替しない)
import { serveStatic } from '@hono/node-server/serve-static'; app.use('/static/*', serveStatic({ root: './' }));serveStatic:Nodeで静的配信(対応ファイルが必要。cwd基準。秘密ファイルを公開しない)

ほかの公式パックを見る