1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

個人開発の中古バイク検索サービスで「ランキングデータAPI」を公開した話 ― 設計と、ハマったところ

1
Last updated at Posted at 2026-06-27

はじめに

個人で運営している中古バイク横断検索サービス「MotoHub」で、車種別の流通台数ランキングを返すデータAPIを公開しました。

この記事では、その設計・実装と、地味にハマったポイントを技術中心にまとめます。

スタック:Laravel 12 / PHP 8.3 / MySQL / Redis / Meilisearch

何を作ったか

排気量クラス別(125 / 250 / 400 / 大型)に「中古市場で今どの車種が何台流通しているか」のランキングを返すREST APIです。

GET /api/v1/rankings/listings?class=250

レスポンス(イメージ):

{
  "class": "250",
  "updated_at": "2026-06-27T09:00:00+09:00",
  "rankings": [
    { "rank": 1, "model": "レブル250", "count": 1000 },
    { "rank": 2, "model": "...",       "count": 0    }
  ],
  "source": "MotoHub"
}

メディアやYouTuberが「MotoHub調べ」として記事・動画の裏付けに使えるようにするのが目的です。

設計のポイント

1. APIキーはSHA-256でハッシュ保存(平文は発行時のみ)

キーはDBに平文で持たず、SHA-256ハッシュで保存します。発行時に一度だけ平文を表示し、以降は再表示しない方針。照合はリクエストのキーをハッシュ化して突き合わせます。

// ※実装イメージ。実際のコードに合わせて差し替えてください
$plain = Str::random(48);
ApiKey::create([
    'name'     => $name,
    'key_hash' => hash('sha256', $plain),
]);
// 平文 $plain はこの場でだけ表示し、保存しない

// 照合
$key = ApiKey::where('key_hash', hash('sha256', $request->apiKey()))->first();

2. キー単位のスロットリング

レート制限はIPではなくキー単位で 10req/min・100req/day。Laravelのrate limiterをキー単位で適用しています。

3. 集計は1時間キャッシュ(画像生成コマンドと共有)

ランキング集計を毎回DBに投げると重いので、集計ロジックを ClassRankingService に集約し、結果を1時間キャッシュ。

ポイントは、同じサービスをランキング画像生成コマンド(motohub:gen-class-ranking)からも呼んでいること。APIと画像で集計ロジックを一本化することで、出口が増えても数字がブレません。

4. 例外時はgracefulに(スタックトレースを出さない)

データ取得に失敗してもstack traceは返さず、503で簡潔なエラーに丸めます。外部公開APIなので、内部情報を漏らさないことを優先。

5. ヘッダ・クエリの両対応

X-API-Key ヘッダと ?api_key= クエリの両方を受け付けます。ブラウザのURLで叩いて動作確認できるよう、あえてクエリも許可しています(相手に使い方を説明するときも楽)。

ハマったところ:排気量クラスの「境界」

一番ハマったのが、クラスの定義でした。

「250ccクラス」を素直に 126〜250cc で集計すると、150 / 155 / 160cc のスクーター等が混入します。でも「250ccバイク」として見せたいのは実質 226〜250cc。デフォルト範囲のまま出したら、ランキングに意図しない車種が並んでしまいました。

結局、クラスごとに排気量の下限・上限を明示的に持たせて解決。「クラス=キリのいい数字の範囲」と思い込むと事故ります。

※125 / 400クラスの境界は、使う場面が来たらデータを見ながら調整中。現状250は 226〜250 が正確です。

おわりに

  • 集計ロジックをサービスに集約してAPI・画像・コマンドで共有しておくと、出口が増えても破綻しにくい
  • 個人開発でも、外部公開する以上は認証・レート制限・例外処理を最初から入れておくと後がラク
  • ドメイン特有の「区切り」(今回は排気量クラス)は、実データを見て定義しないとズレる

データ系の方への提供も始めています。興味があれば MotoHub まで。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?