API仕様 API spec

モバイルアプリが利用する API を、接続先ごとに分類して一覧化します。 現行設計では Cloudflare Workers BFF(/v1/*、OpenAPI 3.1)がクライアント向けの唯一の API 層で、 複雑なロジックは PostgreSQL RPC(Hyperdrive 経由で Workers から呼び出し)に集約しています。 Supabase は Auth / Realtime / PostgreSQL のみ(SDK 直接使用は Auth と Realtime の 2 系統に限定)。

This page catalogs every API the mobile app talks to, grouped by backend. In the current design the Cloudflare Workers BFF (/v1/*, OpenAPI 3.1) is the single client-facing API surface, with complex logic consolidated into PostgreSQL RPCs (invoked from Workers via Hyperdrive). Supabase is used only for Auth / Realtime / PostgreSQL — direct SDK use is limited to Auth and Realtime.

6.1 API設計方針API design principles

6.2 API一覧API inventory

モバイルが叩く BFF エンドポイント(代表)BFF endpoints the mobile app calls (highlights)

完全な一覧と試し打ちは Swagger UI / Redoc を参照。 ここでは画面との対応を把握しやすい代表的な公開/自分向けエンドポイントのみ抜粋します。

For the full list and interactive tryouts use Swagger UI / Redoc. This table highlights the public and "me" endpoints that map 1-to-1 to screens.

メソッドMethod パスPath 用途Purpose
GET/v1/parking-lots駐車場検索(フィルタ + ページング)Search parking lots (filter + paging)
GET/v1/parking-lots/nearbyGPS 周辺検索(PostGIS RPC)Nearby by GPS (PostGIS RPC)
GET/v1/parking-lots/{id}駐車場詳細(画像・タグ・属性)Parking-lot detail (images, tags, attributes)
GET/v1/parking-lots/{id}/pricing-rules料金ルールPricing rules
POST/v1/parking-lots/{id}/calc-fee料金試算(calc_parking_feeFee estimate (calc_parking_fee)
GET/v1/parking-lots/{id}/reviews承認済みレビュー一覧Approved reviews
POST/v1/parking-lots/{id}/reviewsレビュー投稿(status=pending で作成)Post a review (created as status=pending)
POST/v1/parking-sessions駐車開始(create_parking_sessionStart session (create_parking_session)
POST/v1/parking-sessions/{id}/finalize駐車終了+料金確定End session + confirm fee
POST/v1/parking-sessions/{id}/cancelセッションキャンセル(5 分以内)Cancel session (within 5 min)
GET/v1/me自分のプロフィール+権限スコープOwn profile + permission scopes
PATCH/v1/meプロフィール更新Update profile
POST/v1/me/withdraw退会(匿名化+ステータス変更)Withdraw account (anonymize + status change)
GET/POST/v1/me/saved-parking-lotsお気に入り一覧・追加List / add favorites
DELETE/v1/me/saved-parking-lots/{lotId}お気に入り解除Unfavorite
GET/v1/me/parking-sessions自分の駐車履歴Own parking history
PATCH/v1/me/parking-sessions/{id}メモ・個人評価の更新Update memo / personal rating
GET/v1/me/vehicles車両一覧Own vehicles
POST/PATCH/DELETE/v1/me/vehicles[/{id}]車両登録・更新・ソフト削除Register / update / soft-delete vehicles
GET/v1/me/notifications通知一覧(Realtime 購読対象)Notification list (subject to Realtime)
POST/v1/me/notifications/mark-read一括既読(IDs or before)Bulk mark read (IDs or before)
PUT/v1/me/push-tokensFCM デバイストークン upsertUpsert FCM device token
GET/v1/me/exp自分の EXP / レベルOwn EXP / level
GET/v1/me/badges獲得バッジEarned badges
GET/v1/me/badge-progress進捗中バッジIn-progress badges
GET/v1/me/themes所有テーマOwned themes
POST/v1/me/themes/{id}/applyテーマ適用Apply a theme
GET/v1/me/subscription現行サブスクCurrent subscription
POST/v1/me/subscription/verify-iapIAP レシート検証Verify IAP receipt
GET/v1/codesコードマスター一括(edge キャッシュ)Bulk code master (edge-cached)
GET/v1/subscription-plansプランマスター(公開)Plan master (public)
GET/v1/tagsタグマスターTag master
GET/v1/articles公開記事Published articles
GET/v1/ads配信広告Active ads
GET/v1/sponsors公開スポンサーPublic sponsors
GET/v1/sponsors/nearby周辺スポンサー(PostGIS)Nearby sponsors (PostGIS)
POST/v1/sponsors/{id}/checkinスポンサーチェックインCheck in at sponsor
POST/v1/support/ticketsサポート起票Create support ticket
GET/v1/support/tickets自分のサポートチケットOwn support tickets
POST/v1/error-reports誤情報報告Report incorrect info
POST/v1/storage/upload-urlR2 presigned PUT URL(バイトは Workers を経由しない)R2 presigned PUT URL (bytes skip Workers)
POST/v1/search/aiAI 自然言語検索(AI Gateway 経由、10 req/60 s 制限)AI natural-language search (via AI Gateway, 10 req/60 s)

RPC(PostgreSQL 関数)RPC (PostgreSQL functions)

関数Function 目的Purpose 主要引数Key arguments 戻り値Return value
nearby_parking_lots 指定座標の半径内で駐車場を取得Fetch parking lots within a given radius p_lat, p_lng, p_radius_m, p_filters jsonb, p_limit int setof parking_lots + distance_m
calc_parking_fee 料金シミュレーション(サーバー側整合用)Fee simulation (server-side reconciliation) p_lot_id, p_entry_at, p_exit_at, p_vehicle_type jsonb (total_amount, breakdown)
create_parking_session 駐車セッション生成(冪等)Create parking session (idempotent) p_lot_id, p_vehicle_type, p_start_lat, p_start_lng, p_client_request_id uuid (session_id)
finalize_parking_session 駐車終了・料金確定End parking & confirm fee p_session_id, p_exit_at, p_client_request_id jsonb (total_amount, breakdown)
cancel_parking_session 5分以内のキャンセルCancel within 5 minutes p_session_id boolean
mark_notifications_read 通知を一括既読Bulk mark notifications read p_ids uuid[] or p_before timestamptz int (更新行数)int (rows updated)
withdraw_account 退会処理(匿名化+ステータス変更)Account withdrawal (anonymize + status change) なしnone void

Cloudflare Workers BFF エンドポイント(旧 Edge Functions を吸収)Cloudflare Workers BFF endpoints (absorbed the legacy Edge Functions)

エンドポイントEndpoint 目的Purpose 入力Input 出力Output
POST /v1/search/ai 自然言語→検索条件(LLM 呼び出し、AI Gateway 経由)Natural language → search conditions (LLM via AI Gateway) { query, locale } { location, entry_at?, exit_at?, max_price?, filters[] }
POST /v1/admin/user-notifications/{id}/send FCM 配信を parky-fcm-dispatch キューに enqueue(即時 202 返却、consumer で fan-out)Enqueues FCM fan-out into parky-fcm-dispatch (returns 202 immediately; consumer handles delivery) { notification_id } { enqueued, batches }
POST /v1/verify-iap Apple/Google のレシート検証Apple / Google receipt verification { platform, receipt, product_id } { subscription, status }
POST /v1/storage/upload-url R2 presigned PUT URL 発行+assets INSERTMints an R2 presigned PUT URL and inserts into assets { file_name, mime_type, file_size, category } { asset_id, upload_url, s3_key, public_url, expires_in }
report-crash クラッシュレポート補完(Sentryと二重化)Supplemental crash report (doubled up with Sentry) { stack, device, app_version } { ok }

Realtime 購読Realtime subscriptions

チャネルChannel 用途Purpose
user_notifications:user_id=eq.{uid}通知リアルタイム受信Realtime notification receipt
parking_sessions:user_id=eq.{uid}複数端末間の駐車状態同期Cross-device parking state sync
user_badges:user_id=eq.{uid}バッジ獲得演出トリガBadge-earned animation trigger

外部APIExternal APIs

サービスService エンドポイントEndpoint 用途Purpose
Mapboxapi.mapbox.com/geocoding/v5/…住所→座標address → coordinates
MapboxモバイルSDK (タイル)Mobile SDK (tiles)地図描画map rendering
Cloudflare R2署名付きURL (Edge発行)Signed URL (issued by Edge)画像アップロードimage upload
FCMSDKPush受信push receipt
App Store / Play StoreStoreKit2 / Play BillingIAP

6.3 API詳細仕様API details

RPC: nearby_parking_lots

定義Definition

create or replace function public.nearby_parking_lots(
  p_lat      numeric,
  p_lng      numeric,
  p_radius_m int     default 500,
  p_filters  jsonb   default '{}'::jsonb,
  p_limit    int     default 50
) returns table (
  id            uuid,
  name          text,
  address       text,
  lat           numeric,
  lng           numeric,
  distance_m    numeric,
  avg_rating    numeric,
  review_count  int,
  main_image    text,
  tags          text[]
) language sql stable
as $$
  select pl.id, pl.name, pl.address, pl.lat, pl.lng,
         st_distance(pl.location, st_makepoint(p_lng, p_lat)::geography) as distance_m,
         ...
$$;

フィルタ(p_filters JSONB)Filter (p_filters JSONB)

{
  "max_height_m": 2.1,
  "tags": ["24h", "ev"],
  "open_now": true,
  "max_price": 2000,
  "entry_at": "2026-04-14T23:00:00+09:00",
  "exit_at":  "2026-04-15T09:00:00+09:00"
}

性能Performance

  • PostGIS GiST インデックス必須(CREATE INDEX ON parking_lots USING gist(location);
  • A PostGIS GiST index is required (CREATE INDEX ON parking_lots USING gist(location);).
  • 目標:500m 半径で P95 < 200ms
  • Target: P95 < 200ms for a 500m radius query.
RPC: create_parking_session

定義Definition

create or replace function public.create_parking_session(
  p_lot_id            uuid,
  p_vehicle_type      text,
  p_start_lat         numeric,
  p_start_lng         numeric,
  p_client_request_id uuid
) returns uuid language plpgsql
security definer as $$
declare v_session_id uuid;
begin
  -- 冪等性チェック
  select id into v_session_id
    from parking_sessions
    where client_request_id = p_client_request_id
      and user_id = auth.uid();
  if v_session_id is not null then
    return v_session_id;
  end if;

  -- 同時多重駐車防止
  if exists (select 1 from parking_sessions
             where user_id = auth.uid() and status = 'parking') then
    raise exception 'concurrent_session_not_allowed' using errcode = 'P0001';
  end if;

  insert into parking_sessions (...)
  returning id into v_session_id;

  -- 活動ログ
  insert into user_activity_logs (...) values (...);
  return v_session_id;
end$$;

エラーErrors

  • concurrent_session_not_allowed:既に駐車中セッションが存在
  • concurrent_session_not_allowed: an active parking session already exists.
  • lot_not_found:駐車場が存在しないか非公開
  • lot_not_found: the parking lot does not exist or is not public.
RPC: finalize_parking_session

動作Operation

  1. セッションが自分のもので status = 'parking' であることを確認
  2. Verify the session belongs to the caller and is in status = 'parking'.
  3. calc_parking_fee を内部呼出して料金を算出
  4. Internally call calc_parking_fee to compute the fee.
  5. parking_sessionsended に更新、ended_attotal_amount を記録
  6. Update parking_sessions to ended, recording ended_at and total_amount.
  7. user_activity_logssession_end を INSERT(バッジ/EXPトリガ)
  8. INSERT a session_end row into user_activity_logs (triggers badges / EXP).
  9. 算出された金額と内訳を JSONB で返却
  10. Return the computed amount and breakdown as JSONB.
Cloudflare Workers: ai-search-parse

リクエストRequest

POST /functions/v1/ai-search-parse
Authorization: Bearer <jwt>
Content-Type: application/json

{ "query": "池袋で23時から朝9時まで2000円以下", "locale": "ja" }

レスポンスResponse

{
  "location": "池袋",
  "entry_at": "2026-04-14T23:00:00+09:00",
  "exit_at":  "2026-04-15T09:00:00+09:00",
  "max_price": 2000,
  "filters": [],
  "confidence": 0.94
}

エラー形式Error format

{ "error": { "code": "unparseable", "message": "解釈できませんでした" } }
Cloudflare Workers: verify-iap-receipt

シーケンスSequence

sequenceDiagram
  participant App
  participant EF as Cloudflare Workers
  participant Apple
  participant PG as PostgreSQL
  App->>EF: { platform, receipt, product_id }
  alt platform=ios
    EF->>Apple: verifyReceipt
    Apple-->>EF: { valid, expires_at }
  else platform=android
    EF->>Google: Play Developer API
    Google-->>EF: { valid, expires_at }
  end
  EF->>PG: upsert user_subscriptions
  EF->>PG: update app_users.premium
  EF-->>App: { subscription, status }

6.4 エラー仕様Error spec

統一エラー形式(Workers BFF)Unified error format (Workers BFF)

{
  "error": {
    "code": "string_code",     // スネークケースの機械可読コード
    "message": "日本語メッセージ",
    "details": { ... },        // 任意
    "request_id": "uuid"
  }
}
{
  "error": {
    "code": "string_code",     // snake_case machine-readable code
    "message": "human-readable message",
    "details": { ... },        // optional
    "request_id": "uuid"
  }
}

主要エラーコードKey error codes

コードCode HTTPステータスHTTP status 意味Meaning
unauthorized401JWT無効・未認証Invalid JWT / unauthenticated
forbidden403RLSで拒否Denied by RLS
not_found404対象リソース不在Target resource not found
conflict409状態遷移不正・冪等違反Invalid state transition / idempotency violation
validation_error422入力不正Invalid input
rate_limited429レート超過Rate limit exceeded
unparseable422AI検索で解釈不能AI search could not interpret input
iap_invalid400IAPレシート不正Invalid IAP receipt
server_error500内部エラーInternal error
service_unavailable503メンテナンス・外部依存ダウンMaintenance / external dependency down

DB 由来のエラーコード(Workers が翻訳して返す)DB-derived error codes (translated by Workers)

Workers は PostgreSQL の errcode を上記統一形式の error.code にマップしつつ、HTTP ステータスも適切に変換します。代表的なクラス:

Workers translate PostgreSQL's errcode into the unified shape above (error.code) and choose an appropriate HTTP status. Typical classes:

6.5 レート制限Rate limiting

対象Target 制限Limit 実装Implementation
Workers BFF 全般Workers BFF generalCloudflare 側のバースト制限(未チューニング)Cloudflare burst protection (untuned)CF 標準。ユーザー単位に絞りたい場合は Rate Limiting binding で別途設定CF default. Per-user limits use the dedicated Rate Limiting binding
認証系(signIn/signUp/OTP)Auth endpoints (signIn / signUp / OTP)IP単位 10 req/min10 req/min per IPSupabase Auth 設定Supabase Auth settings
AI検索AI searchユーザー 10 req/min(Free)/ 60 req/min(Plus)10 req/min per user (Free) / 60 req/min (Plus)Cloudflare Workers 内で Redis相当のカウンタRedis-equivalent counter inside the Cloudflare Workers
Push 送信Push dispatchユーザー 5通/min(同種別)5 msgs/min per user (same type)DBトリガ+前回送信時刻チェックDB trigger + last-send timestamp check
サポート送信Support submissionsユーザー 5件/日5 per user per dayRPC内チェックChecked inside RPC