Hono on Cloudflare Workersで7つのAPIを運用している実体験から
API(Application Programming Interface)とは、ソフトウェア同士が情報をやり取りするための**インターフェース(接点)**です。
私たち京谷商会では、HonoというTypeScriptフレームワークを使い、Cloudflare Workers上で7つのAPIを運用しています。kyotani-shoukai-api、haishoku-api、nad-api、portal-workerなど、すべてのバックエンドがHono + D1(CloudflareのSQLiteデータベース)という統一スタックで構築されています。
この記事では、REST APIの基本概念を解説しながら、私たちが実際に運用しているエンドポイントの設計事例を交えて、API開発のリアルな姿をお伝えします。
REST APIとは
REST(Representational State Transfer)は、APIを設計するための最も一般的なアーキテクチャスタイルです。2000年にRoy Fieldingが博士論文で提唱した設計原則で、HTTPプロトコルの特性を最大限に活かすAPI設計が可能になります。RESTの原則に従って設計されたAPIをREST API(またはRESTful API)と呼びます。
RESTの基本原則
- リソース指向: URLでリソース(データ)を表現する
- HTTPメソッド: 操作の種類をHTTPメソッドで表現する
- ステートレス: 各リクエストは独立しており、サーバーはクライアントの状態を保持しない
- 統一インターフェース: 一貫したルールでAPIを設計する
- レイヤードシステム: クライアントは直接サーバーに接続しているか、中間層を経由しているかを意識しない
- キャッシュ可能: レスポンスにキャッシュ可否の情報を含めることで、クライアント側でのキャッシュを可能にする
京谷商会のAPIもこの原則に忠実に設計しています。例えば、ポータル記事を管理するkyotani-shoukai-apiでは、以下のようなリソース設計を採用しています。
REST以外のAPIスタイル
REST APIが主流ですが、用途によって他のスタイルも使われています。
| スタイル | 特徴 | 適した場面 |
|---|---|---|
| REST | リソース指向、HTTPメソッドで操作表現 | 一般的なWebアプリ、CRUD操作中心 |
| GraphQL | クライアントが必要なデータを指定 | 複雑なデータ取得、モバイルアプリ |
| gRPC | Protocol Buffers、高速なバイナリ通信 | マイクロサービス間通信、低レイテンシ要件 |
| WebSocket | 双方向リアルタイム通信 | チャット、リアルタイム通知 |
京谷商会ではRESTを標準としつつ、リアルタイム機能が必要な場合はCloudflare Durable Objectsを検討する方針をとっています。
HTTPメソッドと実際のエンドポイント設計
REST APIでは、HTTPメソッドを使って操作の種類を表現します。
| メソッド | 操作 | 冪等性 | 京谷商会の実例 |
|---|---|---|---|
| GET | データの取得 | あり | 記事一覧、ユーザー情報の取得 |
| POST | データの作成 | なし | 新規記事の投稿、決済の開始 |
| PUT | データの全体更新 | あり | 記事本文の差し替え |
| PATCH | データの部分更新 | 場合による | 記事ステータスの変更(draft→published) |
| DELETE | データの削除 | あり | 記事の削除 |
**冪等性(べきとうせい)**は、同じリクエストを何回送っても結果が同じになる性質です。GETで記事を10回取得しても結果は同じですが、POSTで記事を10回作成すると10記事できてしまいます。この性質を理解しておくと、リトライ処理やエラーハンドリングの設計で役立ちます。
京谷商会のAPI設計実例
私たちのkyotani-shoukai-apiでは、以下のようなURLパターンでエンドポイントを設計しています。
GET /api/articles → 記事一覧を取得
GET /api/articles/:id → 特定の記事を取得
POST /api/articles → 新しい記事を作成
PATCH /api/articles/:id → 記事を部分更新
DELETE /api/articles/:id → 記事を削除
GET /api/articles/:id/related → 関連記事を取得
Honoでは、このルーティングを非常にシンプルに記述できます。
const app = new Hono()
app.get('/api/articles', async (c) => {
const db = c.env.DB // D1データベース
const articles = await db.prepare(
'SELECT id, title, slug FROM portal_articles WHERE status = ?'
).bind('published').all()
return c.json(articles.results)
})
ポイント: Honoのc.env.DBでCloudflare D1に直接アクセスできるため、外部のデータベースサーバーを用意する必要がありません。これがsub-100msのレスポンスを実現している理由の一つです。
URLの命名規則
実務でよく悩むのがURLの命名です。京谷商会では以下のルールを統一しています。
- 複数形の名詞を使う:
/api/articles(/api/articleではない) - ケバブケース:
/api/portal-articles(/api/portalArticlesではない) - 動詞はHTTPメソッドで表現:
POST /api/articlesで作成を表す(/api/createArticleではない) - ネストは2階層まで:
/api/articles/:id/relatedまで。それ以上は別エンドポイントに分離
HTTPステータスコード
APIからのレスポンスには、処理結果を示すステータスコードが含まれます。京谷商会のAPIでも、適切なステータスコードを返すことを徹底しています。
2xx系(成功)
| コード | 意味 | 京谷商会での使用場面 |
|---|---|---|
| 200 OK | 成功 | 記事取得、一覧取得の成功時 |
| 201 Created | 作成成功 | 新規記事の作成成功時 |
| 204 No Content | 成功(本文なし) | 記事削除の成功時 |
4xx系(クライアントエラー)
| コード | 意味 | 実際の場面 |
|---|---|---|
| 400 Bad Request | リクエスト不正 | 必須パラメータが欠けている場合 |
| 401 Unauthorized | 認証が必要 | LINE Login認証トークンがない場合 |
| 403 Forbidden | アクセス禁止 | 管理者専用エンドポイントへの一般ユーザーアクセス |
| 404 Not Found | リソースが見つからない | 存在しない記事IDへのアクセス |
| 409 Conflict | 競合 | 楽観ロックでバージョン不一致が発生した場合 |
| 429 Too Many Requests | リクエスト過多 | レート制限に達した場合 |
5xx系(サーバーエラー)
| コード | 意味 |
|---|---|
| 500 Internal Server Error | サーバー内部エラー |
| 502 Bad Gateway | ゲートウェイエラー |
| 503 Service Unavailable | サービス利用不可 |
エラーレスポンスの設計
ステータスコードだけでなく、レスポンスボディにもエラーの詳細を含めることが重要です。京谷商会では以下の構造を標準にしています。
{
"error": {
"code": "ARTICLE_NOT_FOUND",
"message": "指定された記事が見つかりません"
}
}
codeはプログラムが判定に使う文字列、messageは人間が読むための説明です。このパターンはMicrosoft REST API Guidelinesでも推奨されており、クライアント側のエラーハンドリングが格段に楽になります。
API認証の基本 — 京谷商会の認証設計
多くのAPIは、不正アクセスを防ぐために認証を要求します。京谷商会では用途に応じて複数の認証方式を使い分けています。
APIキー認証
内部のWorker間通信やバッチ処理には、APIキー認証を使用しています。Cloudflare WorkersのSecrets機能で環境変数として管理し、ソースコードには一切含めません。
Authorization: Bearer your-api-key-here
重要: APIキーは絶対にソースコードに直接書かず、環境変数で管理しましょう。GitリポジトリにAPIキーをコミットしてしまう事故は非常に多いです。Cloudflare Workersではwrangler secret putコマンドで安全にシークレットを設定できます。GitHubではシークレットスキャニングが有効化されていれば、誤ってコミットしたトークンを自動検知してくれます。
OAuth 2.0(LINE Login)
京谷商会のサブスクリプションサービスでは、LINE Login OAuth 2.0でユーザー認証を行っています。ユーザーはLINEアカウントでログインするだけでサービスを利用でき、パスワード管理の負担がありません。
OAuth 2.0の認証フローは以下の通りです。
- ユーザーがLINEログインボタンをクリック
- LINEの認証画面にリダイレクト
- ユーザーが承認すると、認可コードが返される
- サーバー側で認可コードをアクセストークンに交換
- アクセストークンでユーザー情報を取得
OAuth 2.0の仕組みはRFC 6749で標準化されており、LINE以外にもGoogle、GitHub、Xなど多くのサービスで同じフローが使えます。自前でパスワード認証を実装するよりも、既存の認証プロバイダーを活用するほうがセキュリティ面で安全です。
Webhook連携(KOMOJU決済)
京谷商会では決済処理にKOMOJUのAPIを使用しています。KOMOJUのWebhookを受け取り、D1データベースの購読ステータスを更新するという、API連携の実践的な例です。
POST /api/webhooks/komoju → 決済完了通知を受信
Webhookのシグネチャ検証を行い、改ざんされたリクエストを拒否することで、セキュリティを確保しています。Webhook受信側ではリクエストボディのHMAC-SHA256ハッシュを計算し、ヘッダーに含まれる署名と比較することで、正当な送信元からのリクエストであることを検証します。
CORSとクロスオリジン通信
フロントエンドからAPIを呼び出す際に避けて通れないのが**CORS**(Cross-Origin Resource Sharing)です。
京谷商会のWebサイト(www.kyotanishokai.co.jp)からAPI(api.kyotanishokai.co.jp)を呼び出す構成は、ドメインが異なるため「クロスオリジン」にあたります。ブラウザのセキュリティポリシーにより、サーバー側でCORSヘッダーを適切に設定しないとリクエストがブロックされます。
// HonoでのCORS設定例
import { cors } from 'hono/cors'
app.use('/api/*', cors({
origin: 'https://www.kyotanishokai.co.jp',
allowMethods: ['GET', 'POST', 'PATCH', 'DELETE'],
allowHeaders: ['Content-Type', 'Authorization'],
credentials: true,
}))
よくあるミス: 開発中にorigin: '*'で全オリジンを許可し、そのまま本番にデプロイしてしまうケースです。認証付きAPIでは必ず許可するオリジンを明示的に指定しましょう。
D1(SQLite)との組み合わせ — データベースアクセスの実際
京谷商会のAPIが高速なのは、Cloudflare D1(エッジで動作するSQLiteデータベース)を使用しているからです。
D1の特徴:
- Cloudflare Workersと同じエッジで動作するため、ネットワーク遅延がほぼゼロ
- SQLiteベースなので、学習コストが低い
- 記事、ユーザー、購読、GTDタスク、決済情報など、すべてのデータをD1で管理
- 無料プランでも5GBのストレージと500万行/日の読み取りが利用可能
// 記事の検索API
app.get('/api/articles/search', async (c) => {
const query = c.req.query('q')
const results = await c.env.DB.prepare(
'SELECT id, title, slug FROM portal_articles WHERE title LIKE ? AND status = ?'
).bind(`%${query}%`, 'published').all()
return c.json(results.results)
})
このシンプルさがHono + D1の魅力です。外部データベースへの接続設定、コネクションプーリング、VPCの設定といった煩雑な作業が一切不要です。サーバーレスのメリットを最大限に享受できる構成といえます。
APIを試す方法
curlコマンド
ターミナルから直接APIを呼び出せます。私たちも開発中はcurlで動作確認することが多いです。
curl https://api.example.com/articles
curl -X POST https://api.example.com/articles \
-H "Content-Type: application/json" \
-d '{"title": "新しい記事", "body": "本文"}'
VS Code REST Client
Visual Studio Codeの拡張機能「REST Client」を使えば、.httpファイルにリクエストを記述してエディタ内から直接実行できます。チームで共有できるため、APIの動作確認ドキュメントとしても機能します。
Postman / Hoppscotch
GUIベースのAPI開発ツールです。リクエストの組み立て、レスポンスの確認、テストの実行などが視覚的に行えます。HoppscotchはオープンソースのWeb版で、インストール不要で試せます。
ブラウザ
GETリクエストであれば、ブラウザのアドレスバーにURLを入力するだけでレスポンスを確認できます。JSON Viewerなどの拡張機能を入れると見やすくなります。
API設計で押さえておきたいポイント
バージョニング
APIは公開後も改善を続けますが、既存のクライアントを壊さないためにバージョニングが重要です。
GET /api/v1/articles
GET /api/v2/articles
京谷商会の社内APIではバージョンプレフィックスを省略していますが、外部公開するAPIを設計する際は/v1/を最初から付けておくことをお勧めします。
ページネーション
大量のデータを返すエンドポイントには、ページネーションを実装します。京谷商会ではoffset方式を採用しています。
GET /api/articles?page=2&per_page=20
レスポンスにはページネーション情報を含めて、クライアントが次のページの有無を判断できるようにします。
OpenAPI仕様書
APIの仕様を文書化する標準規格がOpenAPI Specificationです。バージョン3.1ではJSON Schema 2020-12と完全互換になり、Zodスキーマからの自動生成がロスレスで行えるようになりました。
京谷商会ではHonoの@hono/zod-openapiを活用し、Zodでスキーマを定義するだけでバリデーション・型推論・API仕様書の3つが同時に生成される仕組みを構築しています。
まとめ — APIは「使う」だけでなく「作る」時代
APIはモダンなWeb開発の基盤技術です。京谷商会では、Hono on Cloudflare Workers + D1という構成で7つのAPIを運用し、18のポータルサイト、決済処理、LINE認証、外部サービスとのAPI連携をすべてREST APIで実現しています。
重要なのは、APIの概念を理解したら、実際に自分で作ってみることです。Honoなら数十行のコードでAPIが作れますし、Cloudflare Workersの無料プランで本番運用まで可能です。
API開発を始める方は、Webサイトの表示速度への影響も意識しながら設計することをお勧めします。バージョン管理にはGit/GitHubを活用し、npm run deployで本番反映できる開発フローを構築しましょう。