データモデルData model

Web 版は Supabase を読み取り専用で使います。スキーマの全容は 共通データモデル を参照してください。 ここでは Web 版が ビルド時 / ランタイムにどのテーブルをどう使うかだけを示します。

The web app reads from Supabase only. For the full schema see the shared data model. This page only lists which tables the web app touches at build time vs. runtime.

前提: Premise: 2026-04-19 の Hybrid SSG + SSR 化以降、駐車場・記事系テーブルは ビルド時ではなく毎リクエストの SSR 経路から BFF (/v1/*) 経由で読み出します。「ビルド時」のセクションは prerender=true の SSG ページが触る範囲に縮小されました。 After the 2026-04-19 Hybrid SSG + SSR cutover, parking and article tables are read at request time via SSR through the BFF (/v1/*), not at build. The "build-time" section below now only covers what prerender=true SSG pages need.
flowchart LR
  subgraph SSG["SSG ページ (ビルド時に確定)"]
    sgs["/, /search, /scene/*, /media/, /about/*, /for-owners/, /privacy, /terms, ..."]
  end
  subgraph SSR["SSR ページ (毎リクエスト)"]
    ssrs["/p/*, /spot/[id]/, /media/[...slug]/, /media/category/[slug]/, /media/story/[...slug]/, /sitemap-pages.xml"]
  end
  subgraph Browser["ブラウザ Island"]
    isl["SearchIsland (Pagefind), ParkingMapIsland (Mapbox)"]
    nl["MediaNewsletter (Supabase REST 直)"]
  end

  SSG -->|build 時のみ| BFF["Workers BFF
/v1/*"] SSR -->|毎リクエスト| BFF isl -->|/search の二段検索や ETA| BFF nl -->|POST /rest/v1/newsletter_subscribers
(anon, RLS で INSERT-only)| Supa[("Supabase
PostgreSQL")] BFF --> Supa

SSG ビルド時に参照するテーブル(BFF 経由)Tables read at build time by SSG pages (via BFF)

Table用途Purpose叩く endpoint (例)Endpoint (example)
articlesトップ・/media//media/story/ 一覧、/about/editors/[slug] の執筆記事リストHighlight rails on top page and /media/, /media/story/ indexes, and the editor's article list/v1/articles/v1/articles
parking_lots + parking_lot_*トップの「おすすめ駐車場」セクションが先に件数だけ取りに行くケース等。詳細はビルド時には積まないUsed for "featured lots" rails on the top page (counts / lightweight rows). Detail pages are no longer baked at build/v1/parking-lots, /v1/hubs/publishable/v1/parking-lots, /v1/hubs/publishable
codes列挙値ラベル(カテゴリ・支払方法・タグ等)の事前解決Pre-resolves enum labels (categories, payment methods, tags)/v1/codes/v1/codes
tagsフィルタチップ・スポットアイコン凡例Filter chips and spot icon legends/v1/tags/v1/tags

SSR ページが参照するテーブル(毎リクエスト、BFF 経由)Tables read by SSR pages (per request, via BFF)

Table使うページPages主な endpointEndpoint
parking_lotsスポット詳細 / 駅ハブ / 都道府県・市区町村ハブSpot detail / station / pref / city hubs/v1/parking-lots, /v1/hubs/*, /v1/parking/{id}/detail (DEPRECATED 2026-07-31)/v1/parking-lots, /v1/hubs/*
parking_lot_images / _hours / _pricing_rulesスポット詳細の付随情報Spot detail details/v1/parking-lots/{id} 集約レスポンス/v1/parking-lots/{id} aggregate
parking_lot_tags + tags設備・支払・訴求バッジ。旧 parking_lot_attributes / parking_lot_payment_methods は 028 / 057 で tags に統合済みFacility / payment / marketing chips. Legacy parking_lot_attributes / parking_lot_payment_methods were folded into tags in 028 / 057/v1/tags/v1/tags
parking_reviewsスポット詳細の星評価・レビュー抜粋Star rating + review excerpts on spot detail/v1/reviews, /v1/parking-lots/{id}/reviews/v1/reviews, /v1/parking-lots/{id}/reviews
articles/media/[...slug]/ 記事本体、/media/category/[slug]/ 一覧、/media/story/[...slug]/Article bodies, category index, story sub-section/v1/articles, /v1/articles/{slug}/v1/articles, /v1/articles/{slug}
jp_holidaysスポット詳細の本日祝日バッジ・営業可否判定Today-holiday badge and open/closed derivation on spot detail/v1/jp-holidays/v1/jp-holidays
sponsors + nearby_sponsors() RPCスポット詳細・駅ハブのエリアスポンサーカード / マップアイコン(PostGIS 距離検索)Area sponsor cards / map icons on spot and station-hub pages (PostGIS distance)/v1/sponsors/v1/sponsors

ランタイム(ブラウザ Island)が触るテーブルTables read by runtime (browser islands)

RLS ポリシー: RLS policies: 今後 anon キーで読み込む island を追加する場合、対象テーブルには deleted_at IS NULL AND published = true 相当の公開条件 RLS を必ず設定する。書き込み許可は INSERT-only / 件数制限など最小権限で。 Any future runtime island that reads via the anon key must rely on RLS that filters to something like deleted_at IS NULL AND published = true. Write access should be INSERT-only with rate limits where applicable.

代表的なクエリパターンTypical queries

スポット詳細の集約 (build)Spot detail aggregate (build)

select pl.*,
       (select json_agg(i) from parking_lot_images i where i.parking_lot_id = pl.id) as images,
       (select json_agg(h) from parking_lot_hours h where h.parking_lot_id = pl.id) as hours,
       (select json_agg(pr) from parking_lot_pricing_rules pr where pr.parking_lot_id = pl.id) as pricing,
       (select avg(rating)::numeric(3,2) from parking_reviews where parking_lot_id = pl.id) as avg_rating
from parking_lots pl
where pl.id = $1 and pl.deleted_at is null;

近隣スポット(PostGIS)Nearby spots (PostGIS)

select id, name, st_distance(location, st_makepoint($1, $2)::geography) as dist_m
from parking_lots
where deleted_at is null
order by location <-> st_makepoint($1, $2)::geography
limit 20;