1. はじめに
現代のバックエンド開発において、「型安全」と「高速な実行速度」は欠かせない要素になっています。その中で今、圧倒的な支持を集めている2つのフレームワークがあります。
- FastAPI: Pythonの型ヒントを駆使し、データ分析やAI連携に強いバックエンドの標準。
- Hono: Web標準(Fetch API)に準拠し、エッジ/サーバーレス環境で圧倒的な軽さを誇るTypeScriptの新星。
言語の違いもあり簡単には比較できず、自分もどちらを使うか考えることが多くなりました。
本記事では、この2つのフレームワークの設計思想の違い、具体的なコード例、そしてデプロイ戦略までを徹底的に比較解説します。
2. 概要比較:基本スペックの決定的な違い
まずは両者の立ち位置を整理します。これらは単なる言語の違いだけでなく、「実行環境(ランタイム)」の思想が根本的に異なります。
| 項目 | FastAPI (Python) | Hono (TypeScript) |
| 主な言語 | Python (3.8+) | TypeScript / JavaScript |
| 型システムの核 | Python Type Hints + Pydantic | TypeScript Native + Zod等 |
| ドキュメント | OpenAPI (Swagger UI) が標準で自動生成 | hono/zod-openapi 等の追加設定が必要 |
| 得意な領域 | AI・機械学習連携、データ処理、重厚なビジネスロジック | エッジコンピューティング、超軽量マイクロサービス、フロントエンドとの型共有(RPC) |
| 主な実行環境 | UvicornなどのASGIサーバー(伝統的なVPS/コンテナ) | Cloudflare Workers、Bun、Deno、AWS Lambda、Node.js(マルチランタイム) |
3. 具体的なコード比較(CRUDの基本例)
「パスパラメータの取得」と「リクエストボディのバリデーション(型チェック)」を行うシンプルなエンドポイントで比較します。
FastAPIのコード例
FastAPIは Pydantic を使ってリクエストのバリデーションを行います。
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel
from typing import Dict
app = FastAPI()
# 擬似データベース(メモリ上の辞書)
# 構造: { user_id: {"name": str, "age": int} }
user_db: Dict[int, dict] = {}
# --- Pydanticモデル(バリデーション用) ---
# 作成・更新時のリクエストボディ用
class UserItem(BaseModel):
name: str
age: int
# レスポンス用(必要に応じてメッセージなどを追加できる型)
class UserResponse(BaseModel):
user_id: int
name: str
age: int
# --- CRUD操作のエンドポイント ---
# 1. CREATE (作成): ユーザーの新規登録
@app.post(
"/users/{user_id}",
response_model=UserResponse,
status_code=status.HTTP_201_CREATED
)
async def create_user(user_id: int, item: UserItem):
if user_id in user_db:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=f"User with ID {user_id} already exists."
)
# データを保存
user_db[user_id] = item.model_dump() # Pydantic v2の書き方 (v1の場合は .dict())
return {
"user_id": user_id,
**user_db[user_id]
}
# 2. READ (読み取り): ユーザー情報の取得
@app.get("/users/{user_id}", response_model=UserResponse)
async def read_user(user_id: int):
if user_id not in user_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
return {
"user_id": user_id,
**user_db[user_id]
}
# 3. UPDATE (更新): ユーザー情報の全更新
@app.put("/users/{user_id}", response_model=UserResponse)
async def update_user(user_id: int, item: UserItem):
if user_id not in user_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
# 既存のデータを上書き
user_db[user_id] = item.model_dump()
return {
"user_id": user_id,
**user_db[user_id]
}
# 4. DELETE (削除): ユーザーの削除
@app.delete("/users/{user_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_user(user_id: int):
if user_id not in user_db:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="User not found"
)
# データを削除
del user_db[user_id]
# 204 No Content を返すため、値は return せず終了します
returnHonoのコード例
Honoは Zod などのバリデータと組み合わせることで、TypeScriptの強力な型安全性を活かせます。
TypeScript
import { Hono } from 'hono'
import { HTTPException } from 'hono/http-exception'
import { zValidator } from '@hono/zod-validator'
import { z } from 'zod'
const app = new Hono()
// 擬似データベース(メモリ上のマップ)
// 構造: Map<number, { name: string; age: number }>
const userDb = new Map<number, { name: string; age: number }>()
// --- Zodスキーマ(バリデーション用) ---
// パスパラメータのバリデーション用(文字列として入ってくるIDを数値に変換)
const paramSchema = z.object({
user_id: z.string().transform((val) => {
const parsed = parseInt(val, 10)
if (isNaN(parsed)) {
throw new HTTPException(400, { message: 'Invalid user_id format' })
}
return parsed
})
})
// リクエストボディ用
const userItemSchema = z.object({
name: z.string().min(1, 'Name is required'),
age: z.number().int().positive()
})
// --- CRUD操作のエンドポイント ---
// 1. CREATE (作成): ユーザーの新規登録
app.post(
'/users/:user_id',
zValidator('param', paramSchema),
zValidator('json', userItemSchema),
(c) => {
const { user_id } = c.req.valid('param')
const { name, age } = c.req.valid('json')
if (userDb.has(user_id)) {
throw new HTTPException(400, { message: `User with ID ${user_id} already exists.` })
}
// データを保存
const newUser = { name, age }
userDb.set(user_id, newUser)
// 201 Created で返却
return c.json({ user_id, ...newUser }, 201)
}
)
// 2. READ (読み取り): ユーザー情報の取得
app.get(
'/users/:user_id',
zValidator('param', paramSchema),
(c) => {
const { user_id } = c.req.valid('param')
const user = userDb.get(user_id)
if (!user) {
throw new HTTPException(404, { message: 'User not found' })
}
return c.json({ user_id, ...user })
}
)
// 3. UPDATE (更新): ユーザー情報の全更新
app.put(
'/users/:user_id',
zValidator('param', paramSchema),
zValidator('json', userItemSchema),
(c) => {
const { user_id } = c.req.valid('param')
const { name, age } = c.req.valid('json')
if (!userDb.has(user_id)) {
throw new HTTPException(404, { message: 'User not found' })
}
// 既存のデータを上書き
const updatedUser = { name, age }
userDb.set(user_id, updatedUser)
return c.json({ user_id, ...updatedUser })
}
)
// 4. DELETE (削除): ユーザーの削除
app.delete(
'/users/:user_id',
zValidator('param', paramSchema),
(c) => {
const { user_id } = c.req.valid('param')
if (!userDb.has(user_id)) {
throw new HTTPException(404, { message: 'User not found' })
}
// データを削除
userDb.delete(user_id)
// 204 No Content を返却 (ボディは空)
return c.body(null, 204)
}
)
export default appここがポイント!
FastAPIは関数に型ヒントを書くだけで、自動的にバリデーションとSwagger UIドキュメントが生成される手軽さがあります。一方、HonoはWeb標準(Request/Response)に近いクリーンなコードになり、フロントエンド(Next.jsなど)とAPIの型を共通化(RPCモード)できる独自の強みを持っています。
4. デプロイとホスティングの最適解
どこにどうやって公開するか(デプロイ戦略)も、この2つの選定において非常に重要です。
FastAPIのデプロイ先
FastAPIはバックグラウンドでプロセスが常駐する「伝統的なサーバー環境」や「コンテナ」が一般的です。
- Docker + VPS(エックスサーバーVPS、Ubuntuなど) / クラウド(AWS EC2など)
- Dockerfile を作成し、UvicornなどのASGIサーバーを動かす構成。インフラを自由に制御したい場合に最適です。
- Render / Railway
- PaaS環境。GitHub連携だけで簡単にPythonアプリをWebサービスとして公開できます。
Honoのデプロイ先
Honoの最大の強みは、「どこでも動く(Multi-runtime)」点です。特にサーバーレス/エッジ環境との相性が抜群です。
- Cloudflare Workers
- Hono開発者がもっとも推奨する環境の一つ。世界中のエッジサーバーでミリ秒以下で起動(コールドスタートなし)し、無料枠も非常に強力です。
- Vercel (Edge Functions)
- Next.jsなどのフロントエンドと一元管理したい場合に最適です。
- AWS Lambda / Bun / Deno Deploy
- 環境に依存しないため、インフラの移行がコード変更なしで行えます。
5. まとめ:どちらを選ぶべきか?
最終的な選定基準をまとめます。
- FastAPIを選ぶべきケース
- プロジェクトで Pythonの豊富なエコシステム(機械学習、データ解析、スクレイピング) を利用したい。
- コードを書くだけで自動生成される Swagger UI(APIドキュメント) が欲しい。
- すでにDockerやVPSなどの固定サーバー環境が手元にある。
- Honoを選ぶべきケース
- TypeScript一本でフルスタックに開発したい(フロントエンドとAPIの型を同期させたい)。
- Cloudflare Workers などのエッジサーバーを活用し、グローバルに超低遅延で、格安(または無料)のインフラ運用をしたい。
- コールドスタート(サーバーレス特有の起動遅延)に悩まされたくない。
結局自分はCloudflare Workersでサーバーレスで無料で運用できるのが一番ポイントで、さらにフロントエンドでNext.jsを使うようになったこともあり、すっかりHonoを選択するようになりました。
Pythonの方が書き方は慣れているのですが運用方法が自分には重要なポイントでした。

コメント