Skip to main content

Supabase

Bookygram uses Supabase for hosted PostgreSQL, Supabase Auth, and Realtime Broadcast. Storage, Edge Functions, Postgres Changes, and the Data API stay off. This page explains where each part sits and the boundaries that keep it that way.

What Supabase provides​

CapabilityUsed forUsed by
PostgresSystem of record for users, profiles, media, and the scaffolded booking schemaAPI, through pg
AuthEmail-and-password sign-in and access tokensWebsite (session), API (verification)
MigrationsOrdered SQL history in supabase/migrations/Supabase CLI
Realtime BroadcastBest-effort event notifications on private channelsAPI (publish), website (one user's channel)
Data API (PostgREST)Not used; stays disabledNobody

Postgres​

The API is the only application process that queries the database. It connects with the pg driver using DATABASE_URL and does not use @supabase/supabase-js or PostgREST for data. See Database connection for the pool lifecycle, Core user and media schema for identity and media tables, and Booking persistence schema for booking state. Booking API flows are not wired to that schema yet.

Schema changes are SQL files under supabase/migrations/, applied with the Supabase CLI (the supabase package in apps/api). The CLI uses DIRECT_URL when it needs a non-pooler connection; the API never reads it. See Manage database migrations.

Auth​

Supabase Auth identifies the person. The application owns everything else.

  1. The website signs a visitor in with email and password through @supabase/supabase-js, using the public project URL and anon key.
  2. The browser sends the session's access token to the API as Authorization: Bearer <token>.
  3. The API verifies the token locally with jose and hands the verified user id to the users slice, which maps it to an application user UUID.

How the API verifies tokens​

The API derives everything from SUPABASE_URL:

  • the issuer is <SUPABASE_URL>/auth/v1;
  • the signing keys are fetched from <SUPABASE_URL>/auth/v1/.well-known/jwks.json and cached by jose.

A token is accepted only when all of these hold:

  • the signature matches a published key (ES256 or RS256);
  • the issuer is the configured project;
  • the token has not expired;
  • the audience and the role claim are both authenticated;
  • the sub claim is a non-empty string.

Anything else, including a missing header, a service-role token, an anonymous token, or a token from another issuer, returns 401 Unauthorized without detail. Token verification needs no anon key or service role key, and it does not call Supabase on each request. Separately, the process calls the Realtime publish endpoint before it listens. For identity, see Authentication. For notifications, see Realtime event channels.

What the website may do​

The website's Supabase client is used for sessions only. It persists and refreshes the session, and does not read tokens from the URL. A guard wraps its fetch and rejects any request to /rest/v1, so application data cannot be read or written through the Data API. If the public URL or anon key is missing, sign-in fails with an "auth is unavailable" error instead of failing at startup. See Sign in on the website.

Identity stays provider-independent​

The Supabase user id is stored as an opaque identity_provider_id on the application user. The user's own UUID is what other tables reference, so replacing the auth provider means remapping that one column. Live events are addressed by that application UUID, not by the Supabase Auth user id.

Realtime​

Supabase Realtime Broadcast carries recorded event notifications. The API publishes them with the server secret. No feature command publishes an event yet. The website can subscribe only to the signed-in application user's private channel, using a short-lived token signed by the project's current ES256 key. Postgres Changes are not used, and browsers do not read business tables through Realtime. See Realtime event channels and Live user events.

Configuration​

VariableProcessVisibilityPurpose
DATABASE_URLAPIPrivateRuntime Postgres connection
DIRECT_URLSupabase CLIPrivateDirect connection for migrations
SUPABASE_URLAPIPrivateProject origin for token verification
NUXT_PUBLIC_SUPABASE_URLWebsitePublicProject origin for sign-in
NUXT_PUBLIC_SUPABASE_ANON_KEYWebsitePublicAnon (publishable) key for the browser auth client
REALTIME_PUBLISH_URLAPIPrivateRealtime broadcast endpoint
REALTIME_SECRET_KEYAPIPrivateServer credential used to publish broadcasts
REALTIME_JWT_SECRETAPIPrivatePrivate half of the project's current ES256 signing key
NUXT_PUBLIC_REALTIME_URLWebsitePublicRealtime websocket URL, with no secrets

SUPABASE_URL must be an absolute origin with no credentials, path, query, or trailing slash. Production requires HTTPS. Never place the service role key, the publish secret, or the signing key in any web variable. See Environment variables for the full reference.

Boundaries​

  • Browsers and Telegram apps never query Postgres or the Data API.
  • Authorization is an application decision: a valid token proves identity, and the API's use cases decide what that user may do.
  • Realtime is limited to Broadcast on the private channels described above. Storage and Edge Functions are not part of the current system. Objects live in S3 and durable background work runs on SQS.