BFF だけを叩く。OpenAPI は openapi-app-mobile.json。
Talk only to the BFF. OpenAPI lives at openapi-app-mobile.json.
モバイルは Parky BFF(/v1/*)のみを叩きます。Supabase の DB を直接叩いたり、
Supabase Edge Functions を叩いたりはしません。認証トークンの取得だけは Supabase Auth を直接使います。
The mobile app talks only to the Parky BFF (/v1/*) — never to Supabase DB or Edge Functions.
The one exception is Supabase Auth, which is used directly to obtain / refresh JWTs.
OpenAPI — 読むべきはこの 1 ファイル OpenAPI — this one file is enough
| 役割Role | ファイルFile | ビューアViewer | |
|---|---|---|---|
| モバイル用(読むのはこれ) | Mobile (read this) | openapi-app-mobile.json (paths 83) |
Swagger UI / Redoc |
| 全チャネル統合版(参考) | Unified spec (for reference) | openapi.json (paths 367) |
Swagger UI / Redoc |
https://dev-api.parky.co.jp(dev)/
https://api.parky.co.jp(prod)/
http://localhost:8787(ローカル)。
Swagger UI の Try-it は常に dev に向きます(本番の誤発火防止)。
Servers declared in OpenAPI:
https://dev-api.parky.co.jp (dev),
https://api.parky.co.jp (prod), and
http://localhost:8787 (local).
Swagger UI's Try it out is pinned to dev (guards against accidental prod calls).
認証フロー(完結版) Authentication flow (end-to-end)
Parky は Supabase Auth によるメール + パスワード認証を採用します。
モバイル側は supabase_flutter SDK でトークン取得/更新のみ行い、
API 呼び出しには取得した JWT を Authorization: Bearer ヘッダに載せて Parky BFF に送ります。
Parky uses Supabase Auth (email + password). The mobile app uses supabase_flutter
only to obtain / refresh the JWT; every BFF call then attaches that JWT as Authorization: Bearer.
① 起動時 — トークン復旧とセッション確認 1. On launch — restore session and verify
sequenceDiagram participant App as Flutter App participant Keystore as Secure Storage
(Keychain / Keystore) participant SbAuth as Supabase Auth participant BFF as Parky BFF (/v1/*) App->>Keystore: read refresh_token alt no token App->>App: show Sign in screen else has token App->>SbAuth: POST /token?grant_type=refresh_token SbAuth-->>App: { access_token, refresh_token, expires_in } App->>Keystore: save tokens App->>BFF: GET /v1/me
Authorization: BearerBFF-->>App: 200 OK { user } App->>App: navigate to Home end
② 401 受信時 — refresh → 自動再試行 2. On 401 — refresh, then retry once
sequenceDiagram participant App as Flutter App participant SbAuth as Supabase Auth participant BFF as Parky BFF App->>BFF: GET /v1/parking-lots?near=...
Authorization: BearerBFF-->>App: 401 { error.code: "unauthorized" } App->>SbAuth: POST /token?grant_type=refresh_token alt refresh OK SbAuth-->>App: { access_token } App->>BFF: retry GET /v1/parking-lots
Authorization: BearerBFF-->>App: 200 OK { items } else refresh failed SbAuth-->>App: 400 invalid_grant App->>App: clear session, navigate to Sign in end
③ ログアウト/退会 3. Logout / account deletion
- ログアウト:
supabase.auth.signOut()→ 端末側のトークン破棄 → Sign in 画面へ - Logout:
supabase.auth.signOut()→ clear local tokens → navigate to Sign in - 退会:
POST /v1/me/withdrawを呼ぶ(BFF が匿名化 +app_users.deleted_atを立てる) → ログアウト処理 - Account deletion: call
POST /v1/me/withdraw(the BFF anonymizes and setsapp_users.deleted_at) → perform logout
強制アップデート契約 Forced-update contract
サーバ側の仕様変更に追従できないクライアントを安全に締め出すため、全 API コールに バージョンヘッダを付けて BFF が強制アップデートを返せるようにします。
To keep obsolete clients from breaking, every BFF call carries a version header, and the server can force an upgrade when the client is too old.
| 項目Item | 仕様Specification | ||
|---|---|---|---|
| クライアント送信ヘッダ | Client-side headers | X-App-Version: 1.2.3 / X-App-Platform: ios|android / X-App-Build: 42 |
|
| サーバ側判定 | Server-side check | BFF が config.minimum_supported_version と比較し、不足なら 426 Upgrade Required を返却 |
BFF compares against config.minimum_supported_version; returns 426 Upgrade Required if too old |
| レスポンス形式 | Response shape | |
|
| クライアント挙動 | Client behavior | 426 を受けたら、すべての操作をブロックする全画面モーダルを表示。store_url への遷移ボタンのみ活性化 |
On 426, show a blocking full-screen modal; only the "Go to store" button is active |
| メンテナンスモード | Maintenance mode | 503 Service Unavailable + error.code: "maintenance" + Retry-After ヘッダ。ユーザーにはメンテナンス画面を表示 |
503 Service Unavailable with error.code: "maintenance" and a Retry-After header. Show a maintenance screen |
横断規約 — 日時・ページネーション・エラー Cross-cutting conventions
日時フォーマット Date/time format
- すべての日時は ISO 8601 文字列、UTC オフセット付き(例:
2026-04-14T23:00:00+09:00)。 - All datetimes are ISO 8601 strings with offset (e.g.
2026-04-14T23:00:00+09:00). - 表示上の既定タイムゾーンは Asia/Tokyo。ユーザーの端末 TZ で表示しますが、BFF とのやり取りはオフセット付き ISO 8601 に統一します。
- Display defaults to Asia/Tokyo for Parky; the device TZ is used for rendering, but the wire format is always ISO 8601 with offset.
- Unix timestamp 不使用。日付のみの場合も
2026-04-14形式。 - Unix timestamps are not used. Date-only fields use
2026-04-14form.
ページネーション Pagination
現状の OpenAPI は page + limit + total 方式(offset pagination)です。
モバイル側で無限スクロールを実装する場合は、下記のクライアント規約に従ってください。
The current OpenAPI uses offset pagination (page + limit + total).
For infinite scroll on mobile, follow the client-side convention below.
- 初回:
?page=1&limit=20を要求 - First call:
?page=1&limit=20 - 追加取得:ユーザーのスクロール末端で
pageを +1 - Next: increment
pagewhen the user reaches the list end - 終了条件:
items.length < limitまたはpage * limit >= total - Stop when
items.length < limitorpage * limit >= total limitは API 既定(多くは 20 件)を尊重、画面別のオーバーライドは不要- Respect the API default for
limit(usually 20); no screen-specific override
エラー形式(統一) Error shape (unified)
BFF の全エンドポイントは下記形式でエラーを返します。詳細カタログは bff/errors.html。
Every BFF endpoint returns errors in this shape. The full catalog lives at bff/errors.html.
{
"error": {
"code": "snake_case_code",
"message": "human readable",
"request_id": "uuid",
"details": { ... }
}
}
| HTTPHTTP | error.code |
意味Meaning | クライアント処理Client behavior | ||
|---|---|---|---|---|---|
| 401 | unauthorized | JWT 不正 / 期限切れ | Invalid / expired JWT | refresh → 1 回だけ再試行 | Refresh → retry once |
| 403 | forbidden | RLS で拒否 | Denied by RLS | 画面内エラー表示 | Show inline error |
| 404 | not_found | 対象リソースなし | Resource not found | 空状態画面へ | Show empty state |
| 409 | conflict | 状態遷移違反・冪等違反 | Invalid transition / idempotency violation | 最新状態を再取得し再試行判断 | Refetch and decide |
| 422 | validation_error | 入力不正 | Invalid input | フィールドエラーとして表示 | Show as field-level error |
| 426 | upgrade_required | 強制アップデート | Forced upgrade | 上記モーダルを出す | Show the forced-upgrade modal |
| 429 | rate_limited | レート超過 | Rate limited | Retry-After に従いバックオフ | Back off by Retry-After |
| 503 | maintenance | メンテナンス中 | Maintenance | メンテナンス画面を出す | Show maintenance screen |
レート制限 Rate limits
POST /v1/search/ai(AI 検索): 10 req / 60 s / ユーザーPOST /v1/search/ai(AI search): 10 req / 60 s / user- その他の
/v1/*は現在ゆるい制限(数百 req/min レベル)。429を受けたらRetry-Afterヘッダで待機 - Other
/v1/*endpoints have loose limits (hundreds of req/min). On429, wait perRetry-After
ヘッダ規約(全リクエスト共通) Header conventions (on every request)
| ヘッダHeader | 値Value | 必須Required | |
|---|---|---|---|
Authorization | Bearer <JWT> | ✅ | |
X-App-Version | semver 形式 | ✅ | |
X-App-Platform | ios / android | ✅ | |
X-App-Build | ビルド番号(整数) | Build number (integer) | ✅ |
Accept-Language | ja / en | ⭕ | |
User-Agent | Parky/<version> (<platform>) | ⭕ |
OpenAPI への追補要望(既知の欠落) Known gaps in the OpenAPI
examples: 現在 0 件。レスポンスの具体例はフィールド定義と本ドキュメントで補完examples: currently 0. Use field definitions plus this document to inferoperationId: 未設定。生成クライアントは URL 由来のメソッド名になります(必要に応じて手動リネーム可)operationId: not set. Generated clients will derive method names from URLs (rename by hand if needed)- ミューテーションの 4xx スキーマ: 一部未宣言(
POST /v1/storage/upload-urlなど)。本ページのエラー形式表を適用 - 4xx schemas on some mutations (e.g.
POST /v1/storage/upload-url) are not declared — use the error shape above