データモデル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 | 主な endpoint | Endpoint |
|---|---|---|---|---|
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)
/searchの全文検索 (SearchIsland) は Pagefind の静的インデックス (dist/pagefind/) のみを fetch。Supabase / BFF へのクエリは出さない/search's full-text search (SearchIsland) only fetches the Pagefind static index atdist/pagefind/; no Supabase / BFF traffic at search time- Mapbox 地図 island は外部 Mapbox API のみを叩き、Parky の DB は触らない
- The Mapbox map island talks to Mapbox APIs only; never to Parky's DB
- 例外:
/media/のニュースレター購読フォーム (MediaNewsletter.astro) だけが Supabase REST (POST /rest/v1/newsletter_subscribers) を anon キーで直叩きする。newsletter_subscribersには RLS で「anon は INSERT のみ・SELECT 不可」を強制 - Exception: the newsletter form (
MediaNewsletter.astro) on/media/calls Supabase REST (POST /rest/v1/newsletter_subscribers) directly with the anon key. RLS onnewsletter_subscribersonly allows INSERT (no SELECT) for anon
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;