Upgrade to Pro — share decks privately, control downloads, hide ads and more …

実用!Hono RPC2026

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.
Avatar for yodaka yodaka
April 22, 2026

実用!Hono RPC2026

Avatar for yodaka

yodaka

April 22, 2026

More Decks by yodaka

Other Decks in Programming

Transcript

  1. 🔥 LT — Hono RPC 実用! Hono RPC2026 TypeScript でAPI

    開発するなら知っておきたいRPC tRPC / oRPC / Hono RPC yodaka 2026.04.21
  2. ABOUT ME 自己紹介 ALBUM · yo_gen ▶ よだか yodaka →

    フリーランスWeb Developer → Next.js / Hono / TypeScript / Flutter / Vue.js → TSKaigiスタッフ → 最近は型パズルと音楽制作 —ギターロック好き必聴のアルバム作りました。聴い てね STAFF · TSKAIGI 2026 TSKaigi 2026 ベルサール羽田空港 2026.05.22—23 2 DAYS 実用!Hono RPC 2026 / yodaka 01 / 19
  3. AGENDA 今日はなす5つのこと 01 Hono RPCとは何か(さくっと) 02 今日の題材 — 予約アプリの構成 03

    RPCで型を渡す — Next.js 側 04 OpenAPIで型を渡す — Flutter 側 05 推しポイント 実用!Hono RPC 2026 / yodaka 03 / 19
  4. 00 — BEFORE RPC そもそもHonoとは Web標準ベースの、小さくて速いフレームワーク • ランタイム差異を吸収するポータビリティ Cloudflare Workers,

    Fastly Compute, Deno, Bun, AWS Lambda,... • Web標準 https://wintercg.org/ (WinterTC) • 軽量・高速 Hono x 402,820 ops/sec ±4.78% (80 runs sampled) hono/tiny preset is under 14kB. Zero dependencies. Web Standards only. • 豊富なミドルウェア、カスタマイズ性 CORS、CSRF、JWT、Logger… • RPCやミドルウェアを使った、フロントエンドとの型共有 import { Hono } from 'hono' const app = new Hono() app.get('/', c => c.text('Hello Hono!') ) app.get('/health', c => c.json({ ok: true }) ) export default app 実用!Hono RPC 2026 / yodaka 04 / 19
  5. 01 — HONO RPC Hono RPCとは Hono本体の、フロントエンドへの型共有の仕組み • Honoのルーターの型をそのままクライアントへ •

    hc<AppType>() で呼び出し • パス・リクエスト・レスポンスまで型がつく • コード生成なし・ランタイムへの影響ほぼゼロ // server import { Hono } from 'hono' const app = new Hono() .get('/posts/:id', c => c.json({ id: c.req.param('id') }) ) export type AppType = typeof app // client import { hc } from 'hono/client' const client = hc<AppType>(baseUrl) const res = await client.posts[':id'] .$get({ param: { id: '1' } }) 実用!Hono RPC 2026 / yodaka 05 / 19
  6. 02 — PROJECT 今日の題材は、ある予約アプリ 飲食店の店舗と来店者が使うシステムをひとつのHonoで捌く WEB Next.js 来店者向け ADMIN Next.js

    店舗管理画面 APP Flutter 来店者ネイティブ pnpm monorepo │ Hono on ECS (Node.js) │ 3 front-ends → 1 Hono 実用!Hono RPC 2026 / yodaka 06 / 19
  7. 02 — PROJECT 型の渡し方は、クライアントごとに2通り Hono ROUTER + TYPES Next.js (Web)

    来店者向け Next.js (Admin) 店舗向け Flutter ネイティブアプリ RPC (hc) RPC (hc) OpenAPI 実用!Hono RPC 2026 / yodaka 07 / 19
  8. 03 — RPC APIを定義する Standard Schema Validatorを使えば、ハンドラ内に型がつく BACKEND/SRC/ADMIN.TS import {

    Hono } from 'hono' import { sValidator } from '@hono/standard-validator' import { z } from 'zod' export const adminRouter = new Hono() .post( '/posts', sValidator('json', z.object({ title: z.string(), body: z.string(), })), (c) => { // ↓ title: string, body: string に推論される const { title, body } = c.req.valid('json') return c.json({ ok: true }) } ) 実用!Hono RPC 2026 / yodaka 08 / 19
  9. 03 — RPC sValidator(Standard Schema) v4.7.0〜: Zod / Valibot /

    ArkType を同じ記法で扱える共通バリデーター • 以前はZod Validatorで書いていた Zodにロックインされ、RPCとOpenAPIで書き味も分かれていた • v4.7.0で @hono/standard-validator 登場 Standard Schema という共通インターフェース経由で統一 • Zod / Valibot / ArkType を同じ sValidator で ライブラリ選定に縛られず、ハンドラ内の型も同じように推論 MIXING ZOD + VALIBOT import { sValidator } from '@hono/standard-validator' import { z } from 'zod' import * as v from 'valibot' app.get('/:slug', sValidator('param', v.object({ slug: v.string(), })), sValidator('query', z.object({ name: z.string(), })), (c) => { // ↓ どちらも型付きで取れる const { slug } = c.req.valid('param') const { name } = c.req.valid('query') return c.json({ slug, name }) } ) 実用!Hono RPC 2026 / yodaka 09 / 19
  10. 03 — RPC 型をエクスポートする ルーターの型をそのまま外に出すだけ BACKEND/SRC/INDEX.TS import { adminRouter, webRouter,

    spRouter } from './routers' const app = new Hono() .route('/sp', spRouter) .route('/web', webRouter) .route('/admin', adminRouter) // 👇 これがクライアントに渡る" 契約" export type AdminAppType = typeof adminRouter export type WebAppType = typeof webRouter pnpm workspace なので、フロントからは import type で拾うだけ。 実用!Hono RPC 2026 / yodaka 10 / 19
  11. 03 — RPC クライアントで型を受け取る hc に型を渡すだけで、型安全なAPIクライアントの出来上がり FRONTEND/LIB/CLIENT.TS import { hc

    } from 'hono/client' import type { AdminAppType } from 'backend/src' export const client = hc< AdminAppType >(baseUrl, { headers: { authorization: `Bearer ${token}` }, }) 実用!Hono RPC 2026 / yodaka 11 / 19
  12. 03 — RPC 呼び出しも、ぜんぶ型安全 FRONTEND/APP/POSTS/PAGE.TSX const res = await client.

    posts.$post ({ json: { // ↓ title/body がサジェスト、型が違うと怒られる title: 'Hello', body: 'Hono is a cool project', }, }) if (res.ok) { const data = await res.json() // ← これも型がつく } PATH パスがサジェストされる INPUT Body / Queryに型 OUTPUT レスポンスも推論 実用!Hono RPC 2026 / yodaka 12 / 19
  13. 03 — RPC SWR / TanStack Queryとも相性◎ parseResponse でレスポンス処理を、$path() でキャッシュキーを、それぞ

    れ型安全に • parseResponse でfetcherが一行に v4.9.0〜: ok判定 + json取り出しを内包、NGなら DetailedError をthrow • $path() をSWRのキャッシュキーに v4.12.0〜: $url()と違いパス文字列をそのまま返す(キー用途にぴったり) • パスパラメータもtype-safeに組み立て client.api.posts[':id'].$path({ param: { id } }) • hookの戻り値は InferResponseType で推論 引数側は InferRequestType で受け取れる FRONTEND/HOOKS/USEPOSTS.TS import { parseResponse, type, type InferResponseType } from 'hono/client' import { client } from '@/lib/client' import useSWR from 'swr' const $get = client.api.posts.$get type Posts = InferResponseType<typeof $get> export function usePosts() { return useSWR<Posts>( client.api.posts.$path() , // ← キャッシュキー () => parseResponse($get()), ) } 実用!Hono RPC 2026 / yodaka 13 / 19
  14. 04 — FLUTTER hono-openapiでルートに説明と型をのせる 素のHonoのまま、describeRoute と validator を挟むだけ SCHEMAS import

    { Hono } from 'hono' import { z } from 'zod' import { describeRoute, resolver, validator, } from 'hono-openapi' const paramsSchema = z.object({ id: z.string(), }) const postSchema = z.object({ id: z.string(), title: z.string().meta({ example: ' 予約完了', }), }) ROUTE export const posts = new Hono().get( '/posts/:id', describeRoute({ description: ' 投稿の取得', responses: { 200: { content: { 'application/json': { schema: resolver(postSchema), }, } }, }, }), validator('param', paramsSchema), (c) => { const { id } = c.req.valid('param') return c.json({ id, title: ' 予約完了' }) } ) 実用!Hono RPC 2026 / yodaka 15 / 19
  15. 04 — FLUTTER ルート定義と実装が、同じチェーンに並ぶ DESCRIBEROUTE + VALIDATOR + HANDLER .get(

    '/posts/:id', describeRoute({ responses: { 200: { content: { 'application/json': { schema: resolver( postSchema ), } }, } } }), validator('param', paramsSchema), (c) => { const { id } = c.req.valid('param') return c.json({ id, title: ' 予約完了' }) } ) ✔ 素のHonoのチェーンを保つのでRPCの型もその まま流れる ✔ validatorで c.req.valid() に 型がのる ✔ describeRouteで OpenAPIスキーマも同じ場所に 集約 実用!Hono RPC 2026 / yodaka 16 / 19
  16. 04 — FLUTTER FlutterクライアントをOpenAPIから生成 01 HonoがOpenAPI JSONをホスト app.get('/openapi', openAPIRouteHandler(app, {…}))

    02 JSON → YAMLへ変換スクリプト CI で実行、差分をPR 化 03 openapi-generator でDartクライ アント生成 Flutter はこのAPI を呼ぶだけ → Flutter側にも、ひと続きの型安全 実用!Hono RPC 2026 / yodaka 17 / 19
  17. 05 — REFLECTIONS Hono RPCの推しポイント 🔥 コード生成ゼロ ルーターの型をそのまま共有。tscが走ればOK。 🧩 Honoに乗せるだけ

    Web標準APIで、Node/Workers/Bun/Deno/Lambda で動く。 ⚡ 書く側の認知負荷が低い 普通にAPIを書くだけで、型がクライアントに届く。 🌐 OpenAPIと"両立"できる RPCが効かない相手(Flutterなど)にはOpenAPI で。 実用!Hono RPC 2026 / yodaka 18 / 19
  18. SUMMARY まとめ → Hono RPC は「Honoのルーター型をそのまま共有する」仕組み → TS同士なら、コード生成なしで型安全に呼び出せる → 相手がTSでない時は、Honoに生成させたOpenAPIを通して型安全に

    → 1つのHonoで、Web / Admin / ネイティブを面倒見られる 🔥 開発体験 × 型安全 × マルチクライアント — ぜんぶ欲しい人にHono RPC 実用!Hono RPC 2026 / yodaka 19 / 19