APIリファレンス API reference

管理者ポータルが使うインターフェースは大きく 4 種類: Cloudflare Workers BFF(/v1/*、OpenAPI 3.1 契約)、 Supabase Auth(SDK 直接)、Supabase Realtime(WebSocket)、外部 API(Mapbox 等)。 詳細な契約は parky/api/openapi.json を参照してください。

The portal talks to four kinds of interfaces: the Cloudflare Workers BFF (/v1/*, OpenAPI 3.1 contract), Supabase Auth (direct SDK), Supabase Realtime (WebSocket), and external APIs (Mapbox etc.). See parky/api/openapi.json for the full contracts.

ベース方針: Baseline: 管理者ポータルのデータアクセスは 2026-04-19 に 100% BFF (/v1/admin/*) 経由へ移行完了。supabase.from() 直叩きは 0 件。Supabase SDK は Auth / Realtime のみ使用。 Admin portal data access is 100% via the BFF (/v1/admin/*) as of 2026-04-19. Zero direct supabase.from() calls. The Supabase SDK is used for Auth and Realtime only.

完全な一覧と試し打ちは Swagger UI / Redoc を参照。 ここでは「画面とエンドポイントの対応」を把握するためのハイライトを掲載します。

For the full list and interactive tryouts use Swagger UI / Redoc. This page highlights the endpoints whose shape lines up 1-to-1 with admin screens.

1. PostgreSQL RPC(Workers から呼び出し)PostgreSQL RPC (invoked from Workers)

関数名Function 入力Inputs 用途Purpose
nearby_parking_lots p_lng, p_lat, p_radius_m GPS 周辺駐車場検索 (PostGIS)GPS proximity search (PostGIS)
parking_lot_engagement_stats 駐車場別エンゲージメント集計 (保存/セッション/検索)Engagement per lot (saves / sessions / searches)
backfill_badge_progress target_badge_id 指定バッジの進捗を全ユーザーで再計算Recompute one badge's progress for every user
backfill_all_badge_progress 全バッジの進捗を再計算Recompute every badge's progress
gift_theme_to_user p_theme_id, p_admin_id, p_user_id, p_message? テーマギフト送信 → theme_gifts + user_themesSend a theme gift; writes theme_gifts + user_themes
recalculate_all_user_levels 全ユーザーのレベルを再計算Recalculate every user's level
evaluate_badge_conditions metadata, conditions (内部) 条件式を JSONB に適用(internal) applies a condition set against JSONB
vault_read_secret secret_id (内部) Vault からシークレットを復号。service_role 専用(internal) Decrypts a Vault secret. service_role only
vault_insert_secret new_secret, new_name (内部) Vault にシークレットを新規登録。service_role 専用(internal) Creates a new Vault secret. service_role only

2. Cloudflare Workers BFF(管理ポータルが主に叩く経路)Cloudflare Workers BFF (the main admin-portal path)

エンドポイントEndpoint リクエストRequest レスポンスResponse 用途Purpose
POST /v1/admin/user-notifications/{id}/send { id } 202 { notification_id, enqueued, batches } FCM 配信を parky-fcm-dispatch キューに投入。consumer が 500 件/バッチで fan-out し、user_notifications.success_count を原子的加算Enqueues FCM fan-out into parky-fcm-dispatch; the consumer dispatches 500 tokens/batch and atomically increments user_notifications.success_count
POST /v1/storage/upload-url { file_name, file_size, mime_type, category, entity_type?, entity_id?, is_public? } { upload_url, asset_id, s3_key, public_url, expires_in } R2 への署名 PUT URL 発行+assets INSERT。クライアントは署名 URL に直接 PUT。公開 GET は cdn.parky.co.jpMints an R2 presigned PUT URL and inserts into assets. Client PUTs directly; public GET via cdn.parky.co.jp
POST /v1/search/ai { action: "parse", message } | { action: "register_key", provider_id, api_key } { status: "parsed", query, reply } | { status: "need_info", reply } | { status: "error", reply } AI 自然言語検索。AI Gateway 経由でマルチプロバイダー・フォールバック。register_key で API キーを Vault に暗号化登録AI natural-language search via AI Gateway with multi-provider fallback. register_key encrypts and stores API keys in Supabase Vault
POST /v1/admin/store-sync/trigger { store: "play_store"|"app_store"|"all", task: "sales"|"metrics"|"reviews"|"all" } 202 { sync_run_ids, count, status: "queued" } ストア同期を parky-store-sync キューに直積分 enqueue。consumer が Google Play / App Store Connect API から取得して store_reviews / store_sales_daily に upsertEnqueues the cartesian product into parky-store-sync; the consumer pulls from Google Play / App Store Connect APIs and upserts store_reviews / store_sales_daily

3. Realtime (WebSocket)

チャネルChannel 監視対象Watched table イベントEvents 用途Purpose
live-user-activity user_activity_logs INSERT アクティビティフィードに即時追加Streams into the activity feed
admin-notifications-sidebar admin_notifications * サイドバー通知バッジの自動更新Auto-refreshes sidebar notification badge

4. Supabase Auth Admin

管理者アカウントの作成・パスワード変更・削除は service_role キーが必須です。 この鍵はクライアントに露出させず、バックオフィス専用の別クライアント (supabaseAdmin) から呼びます。

Creating, updating, or deleting admin accounts requires the service_role key. It must never ship to non-admin clients — call it from the dedicated supabaseAdmin client instead.

OperationEndpoint
ログインLoginPOST /auth/v1/token?grant_type=password
ログアウトLogoutPOST /auth/v1/logout
現在のユーザーCurrent userGET /auth/v1/user
管理者作成Create adminPOST /auth/v1/admin/users service_role
パスワード更新Update passwordPUT /auth/v1/admin/users/{id} service_role
管理者削除Delete adminDELETE /auth/v1/admin/users/{id} service_role

5. 外部 APIExternal APIs

APIエンドポイントEndpoint用途Use
Mapbox Geocoding https://api.mapbox.com/geocoding/v5/mapbox.places/{query}.json 住所 → 座標、逆ジオコードForward / reverse geocoding
Mapbox GL JS https://api.mapbox.com/styles/v1/... 駐車場一覧のマップビジュアライザーMap visualizer for the parking list
Cloudflare R2 S3 compatible 画像・アセットのアップロードImage / asset upload

典型的な使用例 Common usage patterns

1. ページング付き一覧取得Paged list

const { rows, total } = await fetchTablePage({
  table: 'app_users',
  search: { display_name: 'ilike.%john%' },
  order: { column: 'created_at', ascending: false },
  offset: 0,
  limit: 500,
});

2. 駐車場詳細の複数フェッチParking-detail fanout

const [lot, sessions, reviews, reports, engagement] = await Promise.all([
  fetchParkingLot(id),
  fetchParkingSessions({ parkingLotId: id }),
  fetchParkingReviews({ parkingLotId: id }),
  fetchErrorReports({ parkingLotId: id }),
  fetchParkingLotEngagementForLot(id),
]);

3. アセットアップロードAsset upload

// 1. Ask the Workers BFF for a presigned R2 URL
const { upload_url, asset_id, s3_key } = await bff.storage.uploadUrl({
  file_name, file_size, mime_type, category: 'badge_icon', is_public: true,
});
// 2. PUT the file directly to R2
await fetch(upload_url, { method: 'PUT', body: file });
// 3. Link the asset_id on the target row
await updateBadgeDefinition(badge.id, { asset_id });

4. Realtime 購読Subscribe to realtime

const channel = supabase
  .channel('live-user-activity')
  .on('postgres_changes',
      { event: 'INSERT', schema: 'public', table: 'user_activity_logs' },
      (payload) => setLogs(prev => [payload.new, ...prev]))
  .subscribe();