From 9a78130e42db972cd52560fd94a027da2291d473 Mon Sep 17 00:00:00 2001 From: "Andrey A." <56412611+aantti@users.noreply.github.com> Date: Wed, 3 Jun 2026 13:25:48 +0200 Subject: [PATCH 1/7] chore(self-hosted): update 2026-06-03 (#46610) --- docker/CHANGELOG.md | 123 +++++++++++++++++++++--------- docker/CONFIG.md | 2 +- docker/docker-compose.yml | 2 +- docker/utils/add-new-auth-keys.sh | 2 +- docker/versions.md | 89 +++++++++++---------- 5 files changed, 142 insertions(+), 76 deletions(-) diff --git a/docker/CHANGELOG.md b/docker/CHANGELOG.md index e4e98cb9820e7..0149332603a92 100644 --- a/docker/CHANGELOG.md +++ b/docker/CHANGELOG.md @@ -2,10 +2,9 @@ All notable changes to the Supabase self-hosted Docker configuration. -Changes are grouped by service rather than by change type. See [versions.md](./versions.md) -for complete image version history and rollback information. +Changes are grouped by service rather than by change type. See [versions.md](./versions.md) for complete image version history and rollback information. -See per-service updates below for details. +See per-service updates below for details. Only the most important changes relevant to [self-hosted Supabase](https://supabase.com/docs/guides/self-hosting) are included here. For the full list of changes, refer to the release notes and changelogs of each individual service. **Note:** Configuration updates marked with "requires [...] update" are already included in the latest version of the repository. Pull the latest changes or refer to the linked PR for manual updates. After updating `docker-compose.yml`, pull the latest images and recreate containers - use `docker compose pull && docker compose down && docker compose up -d`. @@ -15,16 +14,74 @@ See per-service updates below for details. ⚠️ **Upcoming changes:** Check the main Supabase [changelog](https://github.com/orgs/supabase/discussions/categories/changelog?discussions_q=is%3Aopen+category%3AChangelog+label%3Aself-hosted) for updates: -- [Making Analytics and Vector opt-in](https://github.com/orgs/supabase/discussions/46084) - [Upgrading from PG 15 to 17 (breaking change)](https://github.com/orgs/supabase/discussions/46080) - [Switching Studio from `supabase_admin` to `postgres` (breaking change)](https://github.com/orgs/supabase/discussions/46081) --- +## [2026-06-03] + +⚠️ **Note:** This update includes **important changes**. Please check the details below. + +### Configuration +- ⚠️ Logs and analytics are now [optional](https://github.com/orgs/supabase/discussions/46084) and were removed from the default `docker-compose.yml`. A new `docker-compose.logs.yml` override has been added. Check the main [configuration guide](https://supabase.com/docs/guides/self-hosting/docker#enabling-analytics) and the changes to Studio below for more information - PR [#45327](https://github.com/supabase/supabase/pull/45327) (via [@luizfelmach](https://github.com/luizfelmach/)) +- ⚠️ Added `COMPOSE_FILE` to `.env.example` for configuring compose overrides (also used by `run.sh`) - PR [#45603](https://github.com/supabase/supabase/pull/45603) + +### Documentation +- Added a new [reference list](https://github.com/supabase/supabase/blob/master/docker/CONFIG.md) of all configuration environment variables - PR [#46124](https://github.com/supabase/supabase/pull/46124) +- Updated the main installation and configuration [guide](https://supabase.com/docs/guides/self-hosting/docker) (added "quick start" path and opt-in for logs and analytics; removed the legacy JWT secrets generator) - PR [#46416](https://github.com/supabase/supabase/pull/46416), PR [#45359](https://github.com/supabase/supabase/pull/45359) +- Updated the logs and analytics [how-to guide](https://supabase.com/docs/reference/self-hosting-analytics/introduction) - PR [#46452](https://github.com/supabase/supabase/pull/46452) + +### Utils +- Added `setup.sh` and `run.sh` to support quick start and easier management of the compose configuration - PR [#45603](https://github.com/supabase/supabase/pull/45603) +- Updated `utils/add-new-auth-keys.sh` and `utils/rotate-new-api-key.sh` to remove the dependency on OpenSSL and Node.js - PR [#45941](https://github.com/supabase/supabase/pull/45941) +- Updated `tests/test-container-logs.sh` to skip checks for `kong`, `analytics` and `vector` when the services are not running - PR [#46099](https://github.com/supabase/supabase/pull/46099) + +### API gateway +- Updated Envoy version to `1.38.0` (see `docker-compose.envoy.yml`) - PR [#46023](https://github.com/supabase/supabase/pull/46023) +- Updated Envoy configuration to address a discrepancy in API key checking (requires `volumes/api/envoy` update) - PR [#46023](https://github.com/supabase/supabase/pull/46023) + +### Studio +- Updated to `2026.06.03-sha-0bca601` +- ⚠️ Added `ENABLED_FEATURES_LOGS_ALL` to Studio service configuration (requires `docker-compose.yml` update) - PR [#45327](https://github.com/supabase/supabase/pull/45327) +- ⚠️ Added `SUPABASE_PUBLISHABLE_KEY` and `SUPABASE_SECRET_KEY` to Studio service configuration (requires `docker-compose.yml` update) - PR [#46173](https://github.com/supabase/supabase/pull/46173) +- ⚠️ Added `start_period` to Studio healthcheck for more reliable cold-boot on slower hosts (requires `docker-compose.yml` update) - PR [#45327](https://github.com/supabase/supabase/pull/45327) +- Fixed incorrect connection strings in the connect sheet for self-hosted environments - PR [#46217](https://github.com/supabase/supabase/pull/46217) +- Updated project home and functions page, and added a minimal project settings implementation - PR [#46544](https://github.com/supabase/supabase/pull/46544), PR [#46550](https://github.com/supabase/supabase/pull/46550), PR [#46554](https://github.com/supabase/supabase/pull/46554) + +### Auth +- Updated to `v2.189.0` - [Changelog](https://github.com/supabase/auth/blob/master/CHANGELOG.md) | [Release](https://github.com/supabase/auth/releases/tag/v2.189.0) +- ⚠️ Added `GOTRUE_JWT_ISSUER` to Auth service configuration (requires `docker-compose.yml` update) - PR [#46020](https://github.com/supabase/supabase/pull/46020) + +### PostgREST +- Updated to `v14.12` - [Changelog](https://github.com/PostgREST/postgrest/blob/main/CHANGELOG.md) | [Release](https://github.com/PostgREST/postgrest/releases/tag/v14.12) + +### Realtime +- Updated to `v2.102.3` - [Release](https://github.com/supabase/realtime/releases/tag/v2.102.3) + +### Storage +- Updated to `v1.60.4` - [Release](https://github.com/supabase/storage/releases/tag/v1.60.4) + +### Postgres Meta +- Updated to `v0.96.6` - [Release](https://github.com/supabase/postgres-meta/releases/tag/v0.96.6) + +### Edge Runtime +- Updated to `v1.74.0` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.74.0) + +### Supavisor +- Updated to `2.9.5` - [Release](https://github.com/supabase/supavisor/releases/tag/v2.9.5) +- Added `POSTGRES_HOST` to Supavisor service configuration (requires `docker-compose.yml` and `volumes/pooler/pooler.exs` update) - PR [#41273](https://github.com/supabase/supabase/pull/41273) + +### Analytics (Logflare) +- Updated to `1.43.1` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.43.1) +- ⚠️ Changed default `docker-compose.yml` to no longer include logs & analytics. Read more in Supabase's [changelog](https://github.com/orgs/supabase/discussions/46084) - PR [#45327](https://github.com/supabase/supabase/pull/45327) + +--- + ## [2026-04-27] ### Configuration -- ⚠️ Added `docker-compose.envoy.yml` and `volumes/api/envoy` - PR [#43838](https://github.com/supabase/supabase/pull/43838). See also the API gateway updates below +- ⚠️ Added `docker-compose.envoy.yml` and `volumes/api/envoy`. See also the API gateway updates below - PR [#43838](https://github.com/supabase/supabase/pull/43838) - ⚠️ Changed Studio healthcheck and some other configuration for better compatibility with Podman (requires `docker-compose.yml` update) - PR [#44754](https://github.com/supabase/supabase/pull/44754) - ⚠️ Changed Studio configuration to bind to all IPv4 interfaces only (requires `docker-compose.yml` update) - PR [#44772](https://github.com/supabase/supabase/pull/44772) @@ -34,16 +91,15 @@ See per-service updates below for details. - Updated the main [setup guide](https://supabase.com/docs/guides/self-hosting/docker) and the how-tos to reflect the state of the self-hosted Supabase configuration - PR [#45011](https://github.com/supabase/supabase/pull/45011) ### Utils -- ⚠️ Added `reassign-owner.sh` to update database objects - PR [#42975](https://github.com/supabase/supabase/pull/42975). Read more in the "[Remove superuser access](https://supabase.com/docs/guides/self-hosting/remove-superuser-access)" how-to guide -- ⚠️ Changed `add-new-auth-keys.sh` to also update `docker-compose.yml` - PR [#45056](https://github.com/supabase/supabase/pull/45056) - -### Studio -- Updated to `2026.04.27-sha-5f60601` -- ⚠️ Added 4 new lints to the Security Advisor - PR [#45253](https://github.com/supabase/supabase/pull/45253), PR [#45260](https://github.com/supabase/supabase/pull/45260). Read more about lint rules 0026 - 0029 in the [Performance and Security Advisors](https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0026_pg_graphql_anon_table_exposed) section of the Supabase documentation +- ⚠️ Added `utils/reassign-owner.sh` to update database objects. Read more in the "[Remove superuser access](https://supabase.com/docs/guides/self-hosting/remove-superuser-access)" how-to guide - PR [#42975](https://github.com/supabase/supabase/pull/42975) +- ⚠️ Changed `utils/add-new-auth-keys.sh` to also update `docker-compose.yml` - PR [#45056](https://github.com/supabase/supabase/pull/45056) ### API gateway - ⚠️ Added Envoy as the new optional API gateway (requires `docker-compose.envoy.yml`, `volumes/api/envoy`, and `volumes/logs/vector.yml` update) - PR [#43838](https://github.com/supabase/supabase/pull/43838) (via [@luizfelmach](https://github.com/luizfelmach/)) +### Studio +- Updated to `2026.04.27-sha-5f60601` +- ⚠️ Added 4 new lints to the Security Advisor. Read more about lint rules 0026 - 0029 in the [Performance and Security Advisors](https://supabase.com/docs/guides/database/database-advisors?queryGroups=lint&lint=0026_pg_graphql_anon_table_exposed) section of the Supabase documentation - PR [#45253](https://github.com/supabase/supabase/pull/45253), PR [#45260](https://github.com/supabase/supabase/pull/45260) --- ## [2026-04-08] @@ -52,14 +108,14 @@ See per-service updates below for details. - Added new how-to guides for configuring [custom email templates](https://supabase.com/docs/guides/self-hosting/custom-email-templates), setting up [SAML SSO](https://supabase.com/docs/guides/self-hosting/self-hosted-saml-sso), and [using Postgres 17](https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17) - PR [#42832](https://github.com/supabase/supabase/pull/42832), PR [#43386](https://github.com/supabase/supabase/pull/43386), PR [#44147](https://github.com/supabase/supabase/pull/44147) ### Utils -- ⚠️ Added `upgrade-pg17.sh` - PR [#44147](https://github.com/supabase/supabase/pull/44147). Read more in the "[Upgrade to Postgres 17](https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17)" how-to guide - -### Studio -- Updated to `2026.04.08-sha-205cbe7` +- ⚠️ Added `utils/upgrade-pg17.sh`. Read more in the "[Upgrade to Postgres 17](https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17)" how-to guide - PR [#44147](https://github.com/supabase/supabase/pull/44147) ### API gateway - ⚠️ Added configuration for SAML SSO (requires `.env`, `docker-compose.yml` and `volumes/api/kong.yml` update) - PR [#43385](https://github.com/supabase/supabase/pull/43385) (via [@luizfelmach](https://github.com/luizfelmach/)) +### Studio +- Updated to `2026.04.08-sha-205cbe7` + ### PostgREST - Updated to `v14.8` - [Changelog](https://github.com/PostgREST/postgrest/blob/main/CHANGELOG.md) | [Release](https://github.com/PostgREST/postgrest/releases/tag/v14.8) @@ -73,11 +129,11 @@ See per-service updates below for details. - Updated to `v0.96.3` - [Release](https://github.com/supabase/postgres-meta/releases/tag/v0.96.3) ### Analytics (Logflare) -- Updated to `v1.36.1` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.36.1) +- Updated to `1.36.1` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.36.1) ### Postgres - ⚠️ Added `docker-compose.pg17.yml` override - PR [#44147](https://github.com/supabase/supabase/pull/44147) -- ⚠️ Added `upgrade-pg17.sh` - PR [#44147](https://github.com/supabase/supabase/pull/44147) +- ⚠️ Added `utils/upgrade-pg17.sh` - PR [#44147](https://github.com/supabase/supabase/pull/44147) - ⚠️ Added [documentation](https://supabase.com/docs/guides/self-hosting/postgres-upgrade-17) explaining the upgrade to Postgres 17 --- @@ -87,15 +143,15 @@ See per-service updates below for details. ⚠️ **Note:** This update includes **important changes**. Please check the details below. The following configuration files have been added/updated: `utils/add-new-auth-keys.sh`, `utils/rotate-new-api-keys.sh`, `docker-compose.yml`, `.env.example`, `docker-compose.s3.yml`, `docker-compose.rustfs.yml`, `volumes/api/kong.yml`, `volumes/api/kong-entrypoint.sh`, `docker-compose.caddy.yml`, `docker-compose.nginx.yml`, `volumes/functions/main/index.ts`, and `volumes/proxy`. ### Configuration -- ⚠️ Added scripts and templates to support the new API key format (`sb_` API keys) and the new asymmetric authentication - PR [#43554](https://github.com/supabase/supabase/pull/43554); see the [how-to guide](https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys) for detailed instructions -- Added optional proxy configuration for Caddy and nginx - PR [#43291](https://github.com/supabase/supabase/pull/43291); read the [how-to guide](https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https) to learn more +- ⚠️ Added scripts and templates to support the new API key format (`sb_` API keys) and the new asymmetric authentication. Check the [how-to guide](https://supabase.com/docs/guides/self-hosting/self-hosted-auth-keys) for detailed instructions - PR [#43554](https://github.com/supabase/supabase/pull/43554) +- Added optional proxy configuration for Caddy and nginx. Read the [how-to guide](https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https) to learn more - PR [#43291](https://github.com/supabase/supabase/pull/43291) ### Documentation - Added several new how-to guides to the self-hosted Supabase [documentation](https://supabase.com/docs/guides/self-hosting) - PR [#42745](https://github.com/supabase/supabase/pull/42745), PR [#42953](https://github.com/supabase/supabase/pull/42953), PR [#43177](https://github.com/supabase/supabase/pull/43177), PR [#43286](https://github.com/supabase/supabase/pull/43286), PR [#43293](https://github.com/supabase/supabase/pull/43293) ### Utils and tests -- Added `add-new-auth-keys.sh` and `rotate-new-api-keys.sh` - PR [#43554](https://github.com/supabase/supabase/pull/43554) -- Added `./tests` with 100+ test cases - PR [#43573](https://github.com/supabase/supabase/pull/43573) +- Added `utils/add-new-auth-keys.sh` and `utils/rotate-new-api-keys.sh` - PR [#43554](https://github.com/supabase/supabase/pull/43554) +- Added `tests/` with 100+ test cases - PR [#43573](https://github.com/supabase/supabase/pull/43573) ### Studio - Updated to `2026.03.16-sha-5528817` @@ -112,7 +168,6 @@ See per-service updates below for details. - Updated to `v14.6` - [Changelog](https://github.com/PostgREST/postgrest/blob/main/CHANGELOG.md) | [Release](https://github.com/PostgREST/postgrest/releases/tag/v14.6) ### Realtime - - ⚠️ Added **mandatory** `METRICS_JWT_SECRET` environment variable (requires `docker-compose.s3.yml` update) - PR [realtime#1729](https://github.com/supabase/realtime/pull/1729) ### Storage @@ -172,7 +227,7 @@ See per-service updates below for details. - Updated to `v1.70.3` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.70.3) ### Analytics (Logflare) -- Updated to `v1.31.2` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.31.2) +- Updated to `1.31.2` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.31.2) - ⚠️ Changed default configuration to disable Logflare on `0.0.0.0:4000` to prevent access to `/dashboard` (requires `docker-compose.yml` update). Read more in the "Production Recommendations" section of Logflare [documentation](https://supabase.com/docs/reference/self-hosting-analytics/introduction) - PR [#42857](https://github.com/supabase/supabase/pull/42857) - ⚠️ Changed Kong routes to not include `/analytics/v1` by default (requires `/volumes/api/kong.yml` update) - PR [#42857](https://github.com/supabase/supabase/pull/42857) @@ -225,7 +280,7 @@ See per-service updates below for details. - Updated to `v1.70.0` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.70.0) ### Analytics (Logflare) -- Updated to `v1.30.3` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.30.3) +- Updated to `1.30.3` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.30.3) ### Postgres - No image update @@ -239,8 +294,8 @@ See per-service updates below for details. - Updated self-hosting installation and configuration guide - PR [#40901](https://github.com/supabase/supabase/pull/40901), PR [#41438](https://github.com/supabase/supabase/pull/41438) ### Utils -- Added `generate-keys.sh` - PR [#41363](https://github.com/supabase/supabase/pull/41363) -- Added `db-passwd.sh` - PR [#41432](https://github.com/supabase/supabase/pull/41432) +- Added `utils/generate-keys.sh` - PR [#41363](https://github.com/supabase/supabase/pull/41363) +- Added `utils/db-passwd.sh` - PR [#41432](https://github.com/supabase/supabase/pull/41432) - Changed `reset.sh` to POSIX and added more checks - PR [#41361](https://github.com/supabase/supabase/pull/41361) ### Studio @@ -258,7 +313,7 @@ See per-service updates below for details. - Updated to `v0.95.1` - [Release](https://github.com/supabase/postgres-meta/releases/tag/v0.95.1) ### Analytics (Logflare) -- Updated to `v1.27.0` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.27.0) +- Updated to `1.27.0` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.27.0) - Fixed multiple issues, including a race condition --- @@ -287,7 +342,7 @@ See per-service updates below for details. - Updated to `v1.69.28` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.69.28) ### Analytics (Logflare) -- Updated to `v1.26.25` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.25) +- Updated to `1.26.25` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.25) --- @@ -312,7 +367,7 @@ See per-service updates below for details. - Updated to `v2.65.3` - [Release](https://github.com/supabase/realtime/releases/tag/v2.65.3) ### Analytics (Logflare) -- Updated to `v1.26.13` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.13) +- Updated to `1.26.13` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.13) - Fixed crashdump when `POSTGRES_BACKEND_URL` is malformed - PR [logflare#2954](https://github.com/Logflare/logflare/pull/2954) --- @@ -340,7 +395,7 @@ See per-service updates below for details. - Updated to `v1.69.25` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.69.25) ### Analytics (Logflare) -- Updated to `v1.26.12` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.12) +- Updated to `1.26.12` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.26.12) - Fixed Auth logs query - PR [logflare#2936](https://github.com/Logflare/logflare/pull/2936) - Fixed build configuration to prevent crashes with "Illegal instruction (core dumped)" - PR [logflare#2942](https://github.com/Logflare/logflare/pull/2942) @@ -374,7 +429,7 @@ See per-service updates below for details. - Updated to `v1.69.23` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.69.23) ### Supavisor -- Updated to `v2.7.4` - [Release](https://github.com/supabase/supavisor/releases/tag/v2.7.4) +- Updated to `2.7.4` - [Release](https://github.com/supabase/supavisor/releases/tag/v2.7.4) --- @@ -438,14 +493,14 @@ See per-service updates below for details. - Updated to `v1.69.14` - [Release](https://github.com/supabase/edge-runtime/releases/tag/v1.69.14) ### Supavisor -- Updated to `v2.7.3` - [Release](https://github.com/supabase/supavisor/releases/tag/v2.7.3) +- Updated to `2.7.3` - [Release](https://github.com/supabase/supavisor/releases/tag/v2.7.3) --- ## [2025-10-13] ### Analytics (Logflare) -- Updated to `v1.22.6` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.22.6) +- Updated to `1.22.6` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.22.6) --- @@ -472,7 +527,7 @@ See per-service updates below for details. - Updated to `v0.91.6` - [Release](https://github.com/supabase/postgres-meta/releases/tag/v0.91.6) ### Analytics (Logflare) -- Updated to `v1.22.4` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.22.4) +- Updated to `1.22.4` - [Release](https://github.com/Logflare/logflare/releases/tag/v1.22.4) ### Postgres - Updated to `15.8.1.085` - [Release](https://github.com/supabase/postgres/releases/tag/15.8.1.085) diff --git a/docker/CONFIG.md b/docker/CONFIG.md index 025da3430e348..d139fc984e6d6 100644 --- a/docker/CONFIG.md +++ b/docker/CONFIG.md @@ -850,7 +850,7 @@ The fields below are repeated for each provider. Substitute `` with on | Variable | Type | Set by | Description | Notes | |---|---|---|---|---| -| `API_JWT_JWKS` | JWT | Both | JWKS JSON used to verify tenant JWTs during self-host seeding. Read by `priv/repo/seeds.exs` and `priv/repo/dev_seeds.exs`. | Used only by the seed script (`SEED_SELF_HOST=true`). Required when using the new API keys and new auth. | +| `API_JWT_JWKS` | JWKS | Both | JSON Web Key Set used to verify tenant JWTs during self-host seeding. Read by `priv/repo/seeds.exs` and `priv/repo/dev_seeds.exs`. | Used only by the seed script (`SEED_SELF_HOST=true`). Required when using the new API keys and new auth. | | `API_JWT_SECRET` | string | Both | Symmetric HS256 secret used to sign tokens for the tenant management API and the default self-host tenant. | Required for the tenant management API in production. | | `API_TOKEN_BLOCKLIST` | string (CSV) | Self-hosted | Comma-separated list of tokens blocked from tenant management API access. | Default: empty list. | | `APP_NAME` | string | Both | Application/node name. Used to build the Phoenix endpoint URL host, libcluster DNS basename, and Erlang `RELEASE_NODE`. | Required - raises `APP_NAME not available` if empty. Default: empty (build) / `realtime` (Erlang release script). | diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3f7ea8bfa9045..3e522ccd67d9c 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -14,7 +14,7 @@ services: studio: container_name: supabase-studio - image: supabase/studio:2026.06.01-sha-a4334a2 + image: supabase/studio:2026.06.03-sha-0bca601 restart: unless-stopped healthcheck: test: diff --git a/docker/utils/add-new-auth-keys.sh b/docker/utils/add-new-auth-keys.sh index 67a28486441bf..b728237b7c015 100644 --- a/docker/utils/add-new-auth-keys.sh +++ b/docker/utils/add-new-auth-keys.sh @@ -163,7 +163,7 @@ echo "JWT_KEYS=${JWT_KEYS}" echo "" echo "JWT_JWKS=${JWT_JWKS}" echo "" -echo "To enable asymmetric key pair, the following should be enabled in docker-compose.yml:" +echo "Ensure the following configuration is uncommented in docker-compose.yml for the asymmetric key pair to work:" echo "" echo " Auth: GOTRUE_JWT_KEYS: \${JWT_KEYS:-[]}" echo " Realtime: API_JWT_JWKS: \${JWT_JWKS:-{\"keys\":[]}}" diff --git a/docker/versions.md b/docker/versions.md index 9f330dd5583c2..ee520129b25cc 100644 --- a/docker/versions.md +++ b/docker/versions.md @@ -1,5 +1,16 @@ # Docker Image Versions +## 2026-06-03 +- supabase/studio:2026.06.03-sha-0bca601 (prev supabase/studio:2026.04.27-sha-5f60601) +- supabase/gotrue:v2.189.0 (prev supabase/gotrue:v2.186.0) +- postgrest/postgrest:v14.12 (prev postgrest/postgrest:v14.8) +- supabase/realtime:v2.102.3 (prev supabase/realtime:v2.76.5) +- supabase/storage-api:v1.60.4 (prev supabase/storage-api:v1.48.26) +- supabase/postgres-meta:v0.96.6 (prev supabase/postgres-meta:v0.96.3) +- supabase/edge-runtime:v1.74.0 (prev supabase/edge-runtime:v1.71.2) +- supabase/supavisor:2.9.5 (prev supabase/supavisor:2.7.4) +- supabase/logflare:1.43.1 (prev supabase/logflare:1.36.1) + ## 2026-04-27 - supabase/studio:2026.04.27-sha-5f60601 (prev supabase/studio:2026.04.08-sha-205cbe7) @@ -69,56 +80,56 @@ - supabase/logflare:1.26.12 (prev supabase/logflare:1.22.6) ## 2025-11-12 -- supabase/studio:2025.11.10-sha-5291fe3 (prev 2025.10.27-sha-85b84e0) -- supabase/gotrue:v2.182.1 (prev v2.180.0) -- supabase/realtime:v2.63.0 (prev v2.57.2) -- supabase/storage-api:v1.29.0 (prev v1.28.2) -- supabase/edge-runtime:v1.69.23 (prev v1.69.15) -- supabase/supavisor:2.7.4 (prev 2.7.3) +- supabase/studio:2025.11.10-sha-5291fe3 (prev supabase/studio:2025.10.27-sha-85b84e0) +- supabase/gotrue:v2.182.1 (prev supabase/gotrue:v2.180.0) +- supabase/realtime:v2.63.0 (prev supabase/realtime:v2.57.2) +- supabase/storage-api:v1.29.0 (prev supabase/storage-api:v1.28.2) +- supabase/edge-runtime:v1.69.23 (prev supabase/edge-runtime:v1.69.15) +- supabase/supavisor:2.7.4 (prev supabase/supavisor:2.7.3) ## 2025-10-28 -- supabase/studio:2025.10.27-sha-85b84e0 (prev 2025.10.20-sha-5005fc6) -- supabase/realtime:v2.57.2 (prev v2.56.0) -- supabase/storage-api:v1.28.2 (prev v1.28.1) -- supabase/postgres-meta:v0.93.1 (prev v0.93.0) -- supabase/edge-runtime:v1.69.15 (prev v1.69.14) +- supabase/studio:2025.10.27-sha-85b84e0 (prev supabase/studio:2025.10.20-sha-5005fc6) +- supabase/realtime:v2.57.2 (prev supabase/realtime:v2.56.0) +- supabase/storage-api:v1.28.2 (prev supabase/storage-api:v1.28.1) +- supabase/postgres-meta:v0.93.1 (prev supabase/postgres-meta:v0.93.0) +- supabase/edge-runtime:v1.69.15 (prev supabase/edge-runtime:v1.69.14) ## 2025-10-21 -- supabase/studio:2025.10.20-sha-5005fc6 (prev 2025.10.01-sha-8460121) -- supabase/realtime:v2.56.0 (prev v2.51.11) -- supabase/storage-api:v1.28.1 (prev v1.28.0) -- supabase/postgres-meta:v0.93.0 (prev v0.91.6) -- supabase/edge-runtime:v1.69.14 (prev v1.69.6) -- supabase/supavisor:2.7.3 (prev 2.7.0) +- supabase/studio:2025.10.20-sha-5005fc6 (prev supabase/studio:2025.10.01-sha-8460121) +- supabase/realtime:v2.56.0 (prev supabase/realtime:v2.51.11) +- supabase/storage-api:v1.28.1 (prev supabase/storage-api:v1.28.0) +- supabase/postgres-meta:v0.93.0 (prev supabase/postgres-meta:v0.91.6) +- supabase/edge-runtime:v1.69.14 (prev supabase/edge-runtime:v1.69.6) +- supabase/supavisor:2.7.3 (prev supabase/supavisor:2.7.0) ## 2025-10-13 -- supabase/logflare:1.22.6 (prev 1.22.4) +- supabase/logflare:1.22.6 (prev supabase/logflare:1.22.4) ## 2025-10-08 -- supabase/studio:2025.10.01-sha-8460121 (prev 2025.06.30-sha-6f5982d) -- supabase/gotrue:v2.180.0 (prev v2.177.0) -- postgrest/postgrest:v13.0.7 (prev v12.2.12) -- supabase/realtime:v2.51.11 (prev v2.34.47) -- supabase/storage-api:v1.28.0 (prev v1.25.7) -- supabase/postgres-meta:v0.91.6 (prev v0.91.0) -- supabase/logflare:1.22.4 (prev 1.14.2) -- supabase/postgres:15.8.1.085 (prev 15.8.1.060) -- supabase/supavisor:2.7.0 (prev 2.5.7) +- supabase/studio:2025.10.01-sha-8460121 (prev supabase/studio:2025.06.30-sha-6f5982d) +- supabase/gotrue:v2.180.0 (prev supabase/gotrue:v2.177.0) +- postgrest/postgrest:v13.0.7 (prev postgrest/postgrest:v12.2.12) +- supabase/realtime:v2.51.11 (prev supabase/realtime:v2.34.47) +- supabase/storage-api:v1.28.0 (prev supabase/storage-api:v1.25.7) +- supabase/postgres-meta:v0.91.6 (prev supabase/postgres-meta:v0.91.0) +- supabase/logflare:1.22.4 (prev supabase/logflare:1.14.2) +- supabase/postgres:15.8.1.085 (prev supabase/postgres:15.8.1.060) +- supabase/supavisor:2.7.0 (prev supabase/supavisor:2.5.7) ## 2025-07-15 -- supabase/gotrue:v2.177.0 (prev v2.176.1) -- supabase/storage-api:v1.25.7 (prev v1.24.7) -- supabase/postgres-meta:v0.91.0 (prev v0.89.3) -- supabase/supavisor:2.5.7 (prev 2.5.6) +- supabase/gotrue:v2.177.0 (prev supabase/gotrue:v2.176.1) +- supabase/storage-api:v1.25.7 (prev supabase/storage-api:v1.24.7) +- supabase/postgres-meta:v0.91.0 (prev supabase/postgres-meta:v0.89.3) +- supabase/supavisor:2.5.7 (prev supabase/supavisor:2.5.6) ## 2025-07-02 -- supabase/studio:2025.06.30-sha-6f5982d (prev 2025.06.02-sha-8f2993d) -- supabase/gotrue:v2.176.1 (prev v2.174.0) -- supabase/storage-api:v1.24.7 (prev v1.23.0) -- supabase/supavisor:2.5.6 (prev 2.5.1) +- supabase/studio:2025.06.30-sha-6f5982d (prev supabase/studio:2025.06.02-sha-8f2993d) +- supabase/gotrue:v2.176.1 (prev supabase/gotrue:v2.174.0) +- supabase/storage-api:v1.24.7 (prev supabase/storage-api:v1.23.0) +- supabase/supavisor:2.5.6 (prev supabase/supavisor:2.5.1) ## 2025-06-03 -- supabase/studio:2025.06.02-sha-8f2993d (prev 2025.05.19-sha-3487831) -- supabase/gotrue:v2.174.0 (prev v2.172.1) -- supabase/storage-api:v1.23.0 (prev v1.22.17) -- supabase/postgres-meta:v0.89.3 (prev v0.89.0) +- supabase/studio:2025.06.02-sha-8f2993d (prev supabase/studio:2025.05.19-sha-3487831) +- supabase/gotrue:v2.174.0 (prev supabase/gotrue:v2.172.1) +- supabase/storage-api:v1.23.0 (prev supabase/storage-api:v1.22.17) +- supabase/postgres-meta:v0.89.3 (prev supabase/postgres-meta:v0.89.0) From 19e6e17257d489c67161184fed3a56f4915e750b Mon Sep 17 00:00:00 2001 From: Tina Ha Date: Wed, 3 Jun 2026 08:02:48 -0400 Subject: [PATCH 2/7] docs(access-control): footnote first-branch admin requirement for git flow (#46473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Docs update ## What is the current behavior? The access-control permissions matrix shows Developer-role users can create development branches, but doesn't mention the implicit production-branch registration step that runs on the first "Create branch" click for a gitless project — which is Owner/Administrator only. Customers reading the matrix conclude Developers can branch from the start; in reality, an Owner/Administrator has to perform the first branch creation to register the production branch. ## What is the new behavior? Adds a footnote on the "Development Branches → Create" row that flags the one-time first-branch admin requirement and links to the dashboard branching guide for the full explanation. Short cross-link only — long-form mechanism explanation lives on the dashboard branching guide (companion PR #46471). ## Additional context - Companion PR adding the long-form admonition on the branching guide page: #46471 ## Summary by CodeRabbit * **Documentation** * Clarified branching permissions for dashboard (gitless) workflows: the first branch creation (one-time) without a GitHub integration requires Owner or Administrator to register the production branch; after that initial step, Developers may create, update, and delete branches as normal. --------- Co-authored-by: Claude Opus 4.7 (1M context) Co-authored-by: Chris Chinchilla --- apps/docs/content/guides/platform/access-control.mdx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/docs/content/guides/platform/access-control.mdx b/apps/docs/content/guides/platform/access-control.mdx index e3490f0d70c0e..73bea005159fa 100644 --- a/apps/docs/content/guides/platform/access-control.mdx +++ b/apps/docs/content/guides/platform/access-control.mdx @@ -264,7 +264,7 @@ The table below shows the actions each role can take on the resources belonging | Production Branch | Read | | | | | | | Write | | | | | | Development Branches | List | | | | | -| | Create | | | | | +| | Create[^8] | | | | | | | Update | | | | | | | Delete | | | | | @@ -281,3 +281,5 @@ The table below shows the actions each role can take on the resources belonging [^6]: Listed permissions are for the API and Dashboard. [^7]: Limited to executing SELECT queries. SQL Query Snippets run by the Read-Only role are run against the database using the **supabase_read_only_user**. This role has the [predefined Postgres role pg_read_all_data](https://www.postgresql.org/docs/current/predefined-roles.html). + +[^8]: When using dashboard branching without a GitHub integration, the first branch creation also registers the project's production branch — a one-time step that requires Owner or Administrator. See [Branching via the dashboard](/docs/guides/deployment/branching/dashboard) for details. Developers can create, update, and delete branches normally after that. From 87f880e6696cba968052d29bd91663d2ecc1391f Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Wed, 3 Jun 2026 15:40:53 +0300 Subject: [PATCH 3/7] docs: add guide on reading error.hint in supabase-js (#46556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New guide at `/guides/api/handling-errors-in-supabase-js` that leads with `error.hint` as the most useful field on a Postgres error. When the database knows the fix (a `GRANT` statement to run for a `42501`, a column name you probably meant), it puts the literal SQL in `hint`. Logging only `error.message` hides it. The guide covers the `PostgrestError` shape (fields ordered by usefulness: hint first, message last), branching on `error.code`, and parallel patterns for Auth, Storage, Edge Functions, and Realtime. Linked from the API > Guides sidebar next to the existing PostgREST error codes reference. ## Summary by CodeRabbit * **Documentation** * Added a comprehensive guide on handling errors in supabase-js, explaining the {data, error} pattern and recommending logging full error objects. * Provides a recommended error-handling pattern, guidance to branch on error codes, and example error fields. * Adds component-specific advice for Auth, Storage, Edge Functions, and Realtime, plus a “Related” links section. * Added a navigation entry so the guide appears in the API guides submenu. --- .../NavigationMenu.constants.ts | 4 + .../api/handling-errors-in-supabase-js.mdx | 145 ++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 86b77a828768e..55bef3f1bf76b 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -1571,6 +1571,10 @@ export const api: NavMenuConstant = { { name: 'Generating TypeScript Types', url: '/guides/api/rest/generating-types' }, { name: 'Generating Python Types', url: '/guides/api/rest/generating-python-types' }, { name: 'Error Codes', url: '/guides/api/rest/postgrest-error-codes' }, + { + name: 'Handling Errors in supabase-js', + url: '/guides/api/handling-errors-in-supabase-js', + }, ], }, { diff --git a/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx b/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx new file mode 100644 index 0000000000000..3e816f35b86ba --- /dev/null +++ b/apps/docs/content/guides/api/handling-errors-in-supabase-js.mdx @@ -0,0 +1,145 @@ +--- +id: handling-errors-in-supabase-js +title: 'Handling errors in `supabase-js`' +subtitle: 'Read `error.hint` first — Postgres often tells you the exact fix. Log the full error so you actually see it.' +--- + +Every `supabase-js` call returns a `{ data, error }` pair instead of throwing. When something fails, the single most useful field on `error` is usually `hint` — Postgres returns the _fix_, not just a description of the problem. Logging only `error.message` hides it. + +## Usage of `message` and `hint` properties + +Consider a `42501` permission-denied error on a table where default `GRANT`s have been revoked from `anon`: + +``` +message: "permission denied for table users" +hint: "Grant the required privileges to the current role with: GRANT SELECT ON public.users TO anon;" +``` + +The `message` exposes the error reason, and `hint` gives you the literal SQL statement to run in the dashboard SQL editor to fix it. + +The same pattern shows up across many Postgres errors — missing column? `hint` suggests the column name you probably meant. Type mismatch? `hint` shows the expected type. Whenever Postgres knows the fix, it puts it in `hint`. + +Log the full `error` object, not just `error.message`. + +## The recommended pattern + +Read `{ data, error }` from the response, check `error`, log the whole object, and return early. + +```ts +const { data, error } = await supabase.from('users').select() +if (error) { + console.error(error) + return +} +``` + +In the case of a permission-denied error, the response body will look like this: + +```json +{ + "error": { + "code": "42501", + "message": "permission denied for table users", + "details": null, + "hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.users TO anon;" + }, + "status": 401, + "statusText": "Unauthorized" +} +``` + +`postgrest-js` passes the body through verbatim, so `error.hint` is the exact string Postgres produced. Treat it as the answer the database is giving you, not as a suggestion to file away. + +## The `PostgrestError` fields, by usefulness + +Database calls (`select`, `insert`, `update`, `upsert`, `delete`, `rpc`) return a `PostgrestError` with four fields. Read them in roughly this order: + +| Field | Read it when | +| --------- | ------------------------------------------------------------------------------------------------------------------ | +| `hint` | Always check first. When Postgres includes one, it's the actionable fix (a `GRANT` to run, a column name, a type). | +| `code` | When branching in code. Codes are stable across versions; `message` text isn't. | +| `details` | When `hint` and `message` aren't enough. Often contains the offending value, key, or row. | +| `message` | As the human summary. Useful in UI strings, less useful for debugging. | + +A full list of PostgREST error codes is in the [Error Codes reference](/guides/api/rest/postgrest-error-codes). + +## Branch on `error.code`, not `error.message` + +`error.code` is more reliable than `error.message` for programmatic branching: messages change between Postgres and PostgREST versions, but codes are stable. + +```ts +const { data, error } = await supabase.from('users').select() +if (error) { + console.error(error) + if (error.code === '42501') { + // Permission denied. error.hint usually contains the GRANT to run. + } + return +} +``` + +## Errors from Auth, Storage, and Edge Functions + +The same rule applies across the SDK — log the whole error object — but the shape differs by client. + +### Auth + +`AuthError` exposes `error.code` (e.g. `'invalid_credentials'`, `'email_not_confirmed'`) and `error.status`. Branch on `code`; log the whole thing. + +```ts +const { data, error } = await supabase.auth.signInWithPassword({ + email: 'example@email.com', + password: 'example-password', +}) +if (error) { + console.error(error) + return +} +``` + +### Storage + +`StorageError` exposes `error.statusCode` (HTTP status as a string) and a structured `error` name (e.g. `'Duplicate'`, `'NotFound'`). + +```ts +const { data, error } = await supabase.storage + .from('avatars') + .upload('public/avatar1.png', avatarFile) +if (error) { + console.error(error) + return +} +``` + +### Edge Functions + +Functions errors arrive as one of three subclasses. Narrow with `instanceof`; for `FunctionsHttpError`, parse the body to get the function's own error payload. + +```ts +import { FunctionsFetchError, FunctionsHttpError, FunctionsRelayError } from '@supabase/supabase-js' + +const { data, error } = await supabase.functions.invoke('hello') +if (error instanceof FunctionsHttpError) { + console.error('Function error', await error.context.json()) +} else if (error) { + console.error(error) +} +``` + +### Realtime + +The `subscribe()` callback receives a `status` and, on failure, an `err` argument. Log the whole `err` — its `cause` often holds the underlying reason. + +```ts +supabase.channel('room1').subscribe((status, err) => { + if (status === 'CHANNEL_ERROR' || status === 'TIMED_OUT') { + console.error(status, err) + } +}) +``` + +## Related + +- [PostgREST Error Codes](/guides/api/rest/postgrest-error-codes) +- [Automatic retries with `supabase-js`](/guides/api/automatic-retries-in-supabase-js) +- [Securing your API](/guides/api/securing-your-api) From f8a3a2e28c0235242a10fd77236fc31d28974d88 Mon Sep 17 00:00:00 2001 From: Pamela Chia Date: Wed, 3 Jun 2026 21:07:45 +0800 Subject: [PATCH 4/7] feat(billing): label logs ingest/query in restriction banners (#46609) --- apps/studio/data/usage/constants.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/studio/data/usage/constants.ts b/apps/studio/data/usage/constants.ts index 873e889bbd3fb..61aeabb6e2a84 100644 --- a/apps/studio/data/usage/constants.ts +++ b/apps/studio/data/usage/constants.ts @@ -8,5 +8,7 @@ export const VIOLATION_TYPE_LABELS: Record = { exceed_realtime_connection_count_quota: 'Realtime Connection Count Exceeded', exceed_realtime_message_count_quota: 'Realtime Message Count Exceeded', exceed_storage_size_quota: 'Storage Size Exceeded', + exceed_log_ingestion_quota: 'Logs Ingest Exceeded', + exceed_log_query_quota: 'Logs Query Exceeded', overdue_payment: 'Overdue Payment', } From 04501d2f85af9a8381397f1131cbc65f96211d78 Mon Sep 17 00:00:00 2001 From: Chris Chinchilla Date: Wed, 3 Jun 2026 15:22:38 +0200 Subject: [PATCH 5/7] docs: Allow partial variables to be optional (#46585) ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## Summary by CodeRabbit * **Behavior Changes** * Partial directives now render unprovided variables as empty strings instead of throwing validation errors * Escaped placeholders (preceded by backslash) are preserved and not substituted * Unused provided variables are silently ignored * **Tests** * Updated test coverage to validate the new permissive variable substitution behavior and removed prior error-focused assertions --- apps/docs/features/directives/Partial.test.ts | 79 +++++++++----- apps/docs/features/directives/Partial.ts | 100 +++++------------- 2 files changed, 74 insertions(+), 105 deletions(-) diff --git a/apps/docs/features/directives/Partial.test.ts b/apps/docs/features/directives/Partial.test.ts index 5217554a15ca5..9e0124ddc6ee2 100644 --- a/apps/docs/features/directives/Partial.test.ts +++ b/apps/docs/features/directives/Partial.test.ts @@ -1,7 +1,6 @@ -import { describe, it, expect } from 'vitest' - import { mdxToMarkdown } from 'mdast-util-mdx' import { toMarkdown } from 'mdast-util-to-markdown' +import { describe, expect, it } from 'vitest' import { partialsRemark } from './Partial' import { fromDocsMarkdown } from './utils.server' @@ -116,7 +115,12 @@ Some more text. await expect(partialsRemark()(mdast)).rejects.toThrowError(/valid JSON/) }) - it('should error when required variable is missing', async () => { + it('should render an unprovided variable as an empty string', async () => { + // The variables.mdx fixture reads "Here is a partial that takes a {{ .var }}." + // When `var` is not provided, the `{{ .var }}` placeholder is replaced with + // an empty string rather than throwing. The trailing " ." in the expected + // output is the intended result: the placeholder is gone, leaving nothing + // between "a" and the period. const markdown = ` # Embed partial @@ -126,54 +130,64 @@ Some more text. `.trim() const mdast = fromDocsMarkdown(markdown) - await expect(partialsRemark()(mdast)).rejects.toThrowError( - /Missing required variable in \$Partial ".*variables\.mdx": "var"/ - ) - }) + const transformed = await partialsRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) - it('should error when unexpected variable is provided', async () => { - const markdown = ` + // Note the empty gap where `{{ .var }}` used to be — this is deliberate. + const expected = ` # Embed partial -<$Partial path="/_fixtures/variables.mdx" variables={{ "var": "correct", "extra": "unexpected" }} /> +Here is a partial that takes a . Some more text. -`.trim() +`.trimStart() - const mdast = fromDocsMarkdown(markdown) - await expect(partialsRemark()(mdast)).rejects.toThrowError( - /Unexpected variable in \$Partial ".*variables\.mdx": "extra"/ - ) + expect(output).toEqual(expected) + // The placeholder must be fully removed, not left as literal `{{ .var }}`. + expect(output).not.toContain('{{') }) - it('should error with detailed message for multiple missing variables', async () => { + it('should ignore a variable that is not referenced in the partial', async () => { + // The variables.mdx fixture only references `var`. Providing an additional + // `extra` variable that the partial never uses is silently ignored rather + // than throwing — `var` is substituted and `extra` leaves no trace. const markdown = ` # Embed partial -<$Partial path="/_fixtures/multiple-variables.mdx" variables={{ "var1": "value1" }} /> +<$Partial path="/_fixtures/variables.mdx" variables={{ "var": "correct", "extra": "unused" }} /> Some more text. `.trim() const mdast = fromDocsMarkdown(markdown) - await expect(partialsRemark()(mdast)).rejects.toThrowError( - /Missing required variables.*"var2".*"var3".*Expected variables.*"var1".*"var2".*"var3".*Provided variable: "var1"/s - ) + const transformed = await partialsRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + expect(output).toContain('Here is a partial that takes a correct.') + expect(output).not.toContain('unused') }) - it('should error with detailed message for multiple unexpected variables', async () => { + it('should render only the unprovided variables as empty when some are provided', async () => { + // The multiple-variables.mdx fixture reads: + // "This partial has {{ .var1 }}, {{ .var2 }}, and {{ .var3 }}." + // Only `var1` is provided here, so `var2` and `var3` collapse to empty + // strings while `var1` is substituted normally. const markdown = ` # Embed partial -<$Partial path="/_fixtures/variables.mdx" variables={{ "var": "correct", "extra1": "wrong", "extra2": "also wrong" }} /> +<$Partial path="/_fixtures/multiple-variables.mdx" variables={{ "var1": "value1" }} /> Some more text. `.trim() const mdast = fromDocsMarkdown(markdown) - await expect(partialsRemark()(mdast)).rejects.toThrowError( - /Unexpected variables.*"extra1".*"extra2".*Expected variable: "var".*Provided variables.*"var".*"extra1".*"extra2"/s - ) + const transformed = await partialsRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + // Provided variable is substituted; the two unprovided ones leave empty gaps. + expect(output).toContain('This partial has value1, , and .') + // No placeholder text survives for the unprovided variables. + expect(output).not.toContain('{{') }) it('should succeed when all variables match exactly', async () => { @@ -212,7 +226,12 @@ Some more text. expect(output).toContain('alphanumeric value') }) - it('should error when hyphenated variable is missing', async () => { + it('should render unprovided hyphenated variables as empty', async () => { + // The hyphenated-variables.mdx fixture reads: + // "This partial has {{ .my-var }}, {{ .another_var }}, and {{ .myVar123 }}." + // Only `my-var` is provided, so the underscore and alphanumeric variables + // collapse to empty strings — confirming the empty-substitution behavior + // applies to all supported variable name styles. const markdown = ` # Embed partial @@ -222,8 +241,10 @@ Some more text. `.trim() const mdast = fromDocsMarkdown(markdown) - await expect(partialsRemark()(mdast)).rejects.toThrowError( - /Missing required variables.*"another_var".*"myVar123".*Expected variables.*"my-var".*"another_var".*"myVar123".*Provided variable: "my-var"/s - ) + const transformed = await partialsRemark()(mdast) + const output = toMarkdown(transformed, { extensions: [mdxToMarkdown()] }) + + expect(output).toContain('This partial has value, , and .') + expect(output).not.toContain('{{') }) }) diff --git a/apps/docs/features/directives/Partial.ts b/apps/docs/features/directives/Partial.ts index 15d40004f5409..86001a7ade8f3 100644 --- a/apps/docs/features/directives/Partial.ts +++ b/apps/docs/features/directives/Partial.ts @@ -7,6 +7,10 @@ * Simple string replacement is supported. The replacement strings are * specified using the `variables` field. * + * Variable substitution is optional. Any variable referenced in the partial + * content but not provided is rendered as an empty string, and any variable + * provided but not referenced in the content is ignored. + * * ## Examples * * ### Simple partial @@ -33,14 +37,14 @@ * ``` */ -import { type Root } from 'mdast' -import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx' import { readFile } from 'node:fs/promises' import { join } from 'node:path' +import { PARTIALS_DIRECTORY } from '~/lib/docs' +import { type Root } from 'mdast' +import type { MdxJsxFlowElement } from 'mdast-util-mdx-jsx' import { type Parent } from 'unist' import { visitParents } from 'unist-util-visit-parents' -import { PARTIALS_DIRECTORY } from '~/lib/docs' import { fromDocsMarkdown, getAttributeValue, getAttributeValueExpression } from './utils.server' export function partialsRemark() { @@ -74,67 +78,19 @@ function toFilePath(node: MdxJsxFlowElement) { } /** - * Extracts all variable names expected in the partial content. - * Returns a Set of variable names found in {{ .variableName }} patterns. - * Variable names can contain alphanumeric characters, hyphens, and underscores. - */ -function extractExpectedVariables(content: string): Set { - const variablePattern = /(?() - let match - - while ((match = variablePattern.exec(content)) !== null) { - variables.add(match[1]) - } - - return variables -} - -/** - * Validates that all expected variables are provided and no unexpected variables are included. - * Throws descriptive errors if validation fails. + * Substitutes provided variables into the partial content. Variable + * substitution is optional: any variable referenced in the content but not + * provided is replaced with an empty string, and any variable provided but not + * referenced is ignored. The leading `\` escape (`\{{ .var }}`) opts a + * placeholder out of substitution. */ -function validateVariables( - content: string, - vars: Record | undefined, - partialPath: string -) { - const expectedVars = extractExpectedVariables(content) - const providedVars = vars ? new Set(Object.keys(vars)) : new Set() - - // Check for missing variables - const missingVars = [...expectedVars].filter((v) => !providedVars.has(v)) - if (missingVars.length > 0) { - const varList = missingVars.map((v) => `"${v}"`).join(', ') - const plural = missingVars.length > 1 - throw new Error( - `Missing required variable${plural ? 's' : ''} in $Partial "${partialPath}": ${varList}\n` + - `Expected variable${expectedVars.size > 1 ? 's' : ''}: ${[...expectedVars].map((v) => `"${v}"`).join(', ')}\n` + - `Provided variable${providedVars.size !== 1 ? 's' : ''}: ${providedVars.size > 0 ? [...providedVars].map((v) => `"${v}"`).join(', ') : 'none'}` - ) - } - - // Check for unexpected variables - const unexpectedVars = [...providedVars].filter((v) => !expectedVars.has(v)) - if (unexpectedVars.length > 0) { - const varList = unexpectedVars.map((v) => `"${v}"`).join(', ') - const plural = unexpectedVars.length > 1 - throw new Error( - `Unexpected variable${plural ? 's' : ''} in $Partial "${partialPath}": ${varList}\n` + - `Expected variable${expectedVars.size !== 1 ? 's' : ''}: ${expectedVars.size > 0 ? [...expectedVars].map((v) => `"${v}"`).join(', ') : 'none'}\n` + - `Provided variable${plural ? 's' : ''}: ${[...providedVars].map((v) => `"${v}"`).join(', ')}` - ) - } -} - function substituteVars(content: string, vars: Record | undefined) { - if (vars === undefined) { - return content - } - - for (const [key, value] of Object.entries(vars)) { + for (const [key, value] of Object.entries(vars ?? {})) { content = content.replace(new RegExp(`(?, - string, - ][] + const partialNodes = [] as [Parent, MdxJsxFlowElement, undefined | Record][] const pendingFetches = [] as Promise[] visitParents(tree, 'mdxJsxFlowElement', (node: MdxJsxFlowElement, ancestors) => { @@ -174,9 +125,8 @@ async function fetchPartialsContent(tree: Root) { const filePath = toFilePath(node) const variables = getVariables(node) const fetchTask = readFile(filePath, 'utf-8') - const partialPath = getAttributeValue(node, 'path') as string - partialNodes.push([parent, node, variables, partialPath]) + partialNodes.push([parent, node, variables]) pendingFetches.push(fetchTask) }) @@ -184,21 +134,19 @@ async function fetchPartialsContent(tree: Root) { const nodeContentMap = new Map< MdxJsxFlowElement, - [Parent, string, undefined | Record, string] + [Parent, string, undefined | Record] >() - partialNodes.forEach(([parent, node, variables, partialPath], index) => { - nodeContentMap.set(node, [parent, resolvedContent[index], variables, partialPath]) + partialNodes.forEach(([parent, node, variables], index) => { + nodeContentMap.set(node, [parent, resolvedContent[index], variables]) }) return nodeContentMap } function rewriteNodes( - contentMap: Map, string]> + contentMap: Map]> ) { - for (const [node, [parent, rawContent, vars, partialPath]] of contentMap) { - const trimmedContent = rawContent.trim() - validateVariables(trimmedContent, vars, partialPath) - let content = substituteVars(trimmedContent, vars) + for (const [node, [parent, rawContent, vars]] of contentMap) { + const content = substituteVars(rawContent.trim(), vars) const replacementContent = fromDocsMarkdown(content) parent.children.splice(parent.children.indexOf(node), 1, replacementContent) } From c582d3749c292cdcd5521bc1c5c875b9b8614544 Mon Sep 17 00:00:00 2001 From: Julien Goux Date: Wed, 3 Jun 2026 16:31:42 +0200 Subject: [PATCH 6/7] fix(studio): update S3 Vectors docs links (#46617) ## What changed Updated the S3 Vectors wrapper docs links in Studio to use the canonical `s3_vectors` docs slug instead of `s3-vectors`. ## Why The docs route on current `master` uses `/guides/database/extensions/wrappers/s3_vectors`, so these Studio links should point to the existing page. ## Validation Skipped local checks at request: this is a two-line URL-only update. ## Summary by CodeRabbit * **Documentation** * Fixed documentation links for S3 Vectors integration to direct users to the correct reference guides. Co-authored-by: Ali Waseem --- .../interfaces/Integrations/Wrappers/Wrappers.constants.ts | 2 +- .../Storage/VectorBuckets/CreateVectorBucketDialog.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts index 30146f8a57cd1..3c3a48f045c90 100644 --- a/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts +++ b/apps/studio/components/interfaces/Integrations/Wrappers/Wrappers.constants.ts @@ -1427,7 +1427,7 @@ export const WRAPPERS: WrapperMeta[] = [ description: 'Cloud storage service for high-dimensional vectors', extensionName: 'S3VectorsFdw', label: 'S3 Vectors', - docsUrl: `${DOCS_URL}/guides/database/extensions/wrappers/s3-vectors`, + docsUrl: `${DOCS_URL}/guides/database/extensions/wrappers/s3_vectors`, categories: ['ai_vectors', 'storage'], minimumExtensionVersion: '0.5.6', server: { diff --git a/apps/studio/components/interfaces/Storage/VectorBuckets/CreateVectorBucketDialog.tsx b/apps/studio/components/interfaces/Storage/VectorBuckets/CreateVectorBucketDialog.tsx index fb0f8c40c4260..f62723970cc02 100644 --- a/apps/studio/components/interfaces/Storage/VectorBuckets/CreateVectorBucketDialog.tsx +++ b/apps/studio/components/interfaces/Storage/VectorBuckets/CreateVectorBucketDialog.tsx @@ -209,7 +209,7 @@ export const CreateVectorBucketDialog = ({ Supabase will install the{' '} {wrappersExtensionState !== 'installed' ? 'Wrappers extension and ' : ''} S3 Vectors Wrapper integration on your behalf.{' '} - + Learn more . From e3be344f5c88609eb2c828cdb9692d1999104e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Pozo?= Date: Wed, 3 Jun 2026 11:05:20 -0500 Subject: [PATCH 7/7] fix(examples): read new API keys directly from parsed SUPABASE_SECRET_KEYS (#46604) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Edge Function examples that use the new publishable/secret API keys read them with a double lookup: ```ts const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const secretKey = Deno.env.get(SUPABASE_SECRET_KEYS['default']) // ❌ returns undefined ``` `SUPABASE_SECRET_KEYS` / `SUPABASE_PUBLISHABLE_KEYS` are a JSON object that maps a key name to the **actual key value** (e.g. `{"default":"sb_secret_..."}`), confirmed by: - the self-hosted injection in `docker/docker-compose.yml` (`SUPABASE_SECRET_KEYS: "{\"default\":\"${SUPABASE_SECRET_KEY:-}\"}"`) - the `@supabase/server` SDK README So `SUPABASE_SECRET_KEYS['default']` is already the key. Wrapping it in another `Deno.env.get(...)` looks up an env var named `sb_secret_...`, which doesn't exist, so the value is `undefined` and the examples fail at runtime. ## Fix Unwrap the outer `Deno.env.get(...)` so the key is read directly: ```ts const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const secretKey = SUPABASE_SECRET_KEYS['default'] // ✅ ``` Applied across 23 files (example functions, the `examples/prompts/edge-functions.md` codegen guidance, and two docs guides). The correct `JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)` declaration line is untouched. The generated `apps/docs/examples/` copy regenerates from `examples/` at build time. ## Notes - Docs context: [#46600](https://github.com/supabase/supabase/pull/46600), which documents the same key model. - Follow-up (not in this PR): a few examples send the secret key on the `Authorization: Bearer` header, which the new keys reject. Worth a separate audit. ## Summary by CodeRabbit * **Documentation** * Clarified examples and guides for correctly reading parsed Supabase secret and publishable key maps. * **Examples** * Standardized credential usage across Edge Functions and samples so Supabase clients consistently receive keys from the parsed key maps rather than indirect lookups. --------- Co-authored-by: Chris Chinchilla --- .../guides/ai/examples/huggingface-image-captioning.mdx | 2 +- .../functions/examples/elevenlabs-transcribe-speech.mdx | 2 +- .../supabase/functions/image_gen/index.ts | 2 +- .../supabase/functions/generate-embedding/index.ts | 2 +- .../ai/edge-functions/supabase/functions/search/index.ts | 2 +- .../supabase/functions/background-upload-storage/index.ts | 5 +---- .../supabase/functions/elevenlabs-speech-to-text/index.ts | 2 +- .../supabase/functions/elevenlabs-text-to-speech/index.ts | 5 +---- .../supabase/functions/file-upload-storage/index.ts | 2 +- .../supabase/functions/get-tshirt-competition/index.ts | 2 +- .../supabase/functions/huggingface-image-captioning/index.ts | 2 +- .../supabase/functions/og-image-with-storage-cdn/handler.tsx | 2 +- .../supabase/functions/openai-image-generation/index.ts | 2 +- .../edge-functions/supabase/functions/read-storage/index.ts | 2 +- .../edge-functions/supabase/functions/restful-tasks/index.ts | 2 +- .../functions/select-from-table-with-auth-rls/index.ts | 2 +- examples/edge-functions/supabase/functions/sentry/index.ts | 5 +---- .../supabase/functions/tweet-to-image/handler.tsx | 2 +- .../supabase/functions/upstash-redis-ratelimit/index.ts | 2 +- examples/prompts/edge-functions.md | 2 +- .../protomaps/supabase/functions/maps-private/index.ts | 2 +- .../supabase/functions/create-upload-token/index.ts | 2 +- .../expo-push-notifications/supabase/functions/push/index.ts | 5 +---- 23 files changed, 23 insertions(+), 35 deletions(-) diff --git a/apps/docs/content/guides/ai/examples/huggingface-image-captioning.mdx b/apps/docs/content/guides/ai/examples/huggingface-image-captioning.mdx index b6b7b05ea472a..a0f89695d594c 100644 --- a/apps/docs/content/guides/ai/examples/huggingface-image-captioning.mdx +++ b/apps/docs/content/guides/ai/examples/huggingface-image-captioning.mdx @@ -65,7 +65,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default when deployed. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API SECRET KEY - env var exported by default when deployed. - Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + SUPABASE_SECRET_KEYS['default'] ?? '' ) // Construct image url from storage diff --git a/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx b/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx index db8248876c3c1..6cdc51f58caf2 100644 --- a/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx +++ b/apps/docs/content/guides/functions/examples/elevenlabs-transcribe-speech.mdx @@ -120,7 +120,7 @@ const elevenLabsClient = new ElevenLabsClient({ const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const supabase = createClient( Deno.env.get('SUPABASE_URL') || '', - Deno.env.get(SUPABASE_SECRET_KEYS['default']) || '' + SUPABASE_SECRET_KEYS['default'] || '' ) async function scribe({ diff --git a/examples/ai/aws_bedrock_image_gen/supabase/functions/image_gen/index.ts b/examples/ai/aws_bedrock_image_gen/supabase/functions/image_gen/index.ts index 5e7ffc820343b..e20b971cfd5b3 100644 --- a/examples/ai/aws_bedrock_image_gen/supabase/functions/image_gen/index.ts +++ b/examples/ai/aws_bedrock_image_gen/supabase/functions/image_gen/index.ts @@ -58,7 +58,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL')!, // Supabase API SECRET KEY - env var exported by default. - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! + SUPABASE_SECRET_KEYS['default']! ) const { data: upload, error: uploadError } = await supabaseClient.storage diff --git a/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts b/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts index 43143fbcab40e..034bed5830633 100644 --- a/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts +++ b/examples/ai/edge-functions/supabase/functions/generate-embedding/index.ts @@ -15,7 +15,7 @@ const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const supabase = createClient( Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! + SUPABASE_SECRET_KEYS['default']! ) const model = new Supabase.ai.Session('gte-small') diff --git a/examples/ai/edge-functions/supabase/functions/search/index.ts b/examples/ai/edge-functions/supabase/functions/search/index.ts index bd003be2f3be9..2497e88b4fa91 100644 --- a/examples/ai/edge-functions/supabase/functions/search/index.ts +++ b/examples/ai/edge-functions/supabase/functions/search/index.ts @@ -7,7 +7,7 @@ const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const supabase = createClient( Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! + SUPABASE_SECRET_KEYS['default']! ) const model = new Supabase.ai.Session('gte-small') diff --git a/examples/edge-functions/supabase/functions/background-upload-storage/index.ts b/examples/edge-functions/supabase/functions/background-upload-storage/index.ts index 49d5d9a99c70c..49fdb1ea55c0e 100644 --- a/examples/edge-functions/supabase/functions/background-upload-storage/index.ts +++ b/examples/edge-functions/supabase/functions/background-upload-storage/index.ts @@ -4,10 +4,7 @@ import OpenAI from 'https://deno.land/x/openai@v4.68.2/mod.ts' const client = new OpenAI({ apiKey: Deno.env.get('OPENAI_API_KEY')! }) const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) -const supabase = createClient( - Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! -) +const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default']!) type StorageFileApi = ReturnType type StorageUploadPromise = ReturnType diff --git a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts index 6623cb3b5e450..56905387d630e 100644 --- a/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts +++ b/examples/edge-functions/supabase/functions/elevenlabs-speech-to-text/index.ts @@ -19,7 +19,7 @@ const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) const supabase = createClient( Deno.env.get('SUPABASE_URL') || '', - Deno.env.get(SUPABASE_SECRET_KEYS['default']) || '' + SUPABASE_SECRET_KEYS['default'] || '' ) async function scribe({ diff --git a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts index 972e449ef8b01..b142f0b0f1039 100644 --- a/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts +++ b/examples/edge-functions/supabase/functions/elevenlabs-text-to-speech/index.ts @@ -5,10 +5,7 @@ import { ElevenLabsClient } from 'npm:elevenlabs@1.52.0' import * as hash from 'npm:object-hash' const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) -const supabase = createClient( - Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! -) +const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default']!) const client = new ElevenLabsClient({ apiKey: Deno.env.get('ELEVENLABS_API_KEY'), diff --git a/examples/edge-functions/supabase/functions/file-upload-storage/index.ts b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts index 37dee7427f93b..80bef78d864a4 100644 --- a/examples/edge-functions/supabase/functions/file-upload-storage/index.ts +++ b/examples/edge-functions/supabase/functions/file-upload-storage/index.ts @@ -29,7 +29,7 @@ app.use(async (ctx) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL')!, // Supabase publishable key - env var exported by default. - Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default'])! + SUPABASE_PUBLISHABLE_KEYS['default']! ) //upload image to Storage diff --git a/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts b/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts index de5dc1c2b020b..20e8f0d4f5dc1 100644 --- a/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts +++ b/examples/edge-functions/supabase/functions/get-tshirt-competition/index.ts @@ -61,7 +61,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default when deployed. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API SECRET KEY - env var exported by default when deployed. - Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + SUPABASE_SECRET_KEYS['default'] ?? '' ) // Submit email to draw const { error } = await supabaseAdminClient.from('get-tshirt-competition-2').upsert( diff --git a/examples/edge-functions/supabase/functions/huggingface-image-captioning/index.ts b/examples/edge-functions/supabase/functions/huggingface-image-captioning/index.ts index 15455577c2bdd..790ee619019e3 100644 --- a/examples/edge-functions/supabase/functions/huggingface-image-captioning/index.ts +++ b/examples/edge-functions/supabase/functions/huggingface-image-captioning/index.ts @@ -23,7 +23,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default when deployed. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API SECRET KEY - env var exported by default when deployed. - Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + SUPABASE_SECRET_KEYS['default'] ?? '' ) // Construct image url from storage diff --git a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx index 3cea004a04c4d..1252b04c072dd 100644 --- a/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx +++ b/examples/edge-functions/supabase/functions/og-image-with-storage-cdn/handler.tsx @@ -201,7 +201,7 @@ export async function handler(req: Request) { // Supabase API URL - env var exported by default when deployed. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API SECRET KEY - env var exported by default when deployed. - Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + SUPABASE_SECRET_KEYS['default'] ?? '' ) // Upload image to storage. diff --git a/examples/edge-functions/supabase/functions/openai-image-generation/index.ts b/examples/edge-functions/supabase/functions/openai-image-generation/index.ts index 8117c2edf72ee..0c5f46aaa3f60 100644 --- a/examples/edge-functions/supabase/functions/openai-image-generation/index.ts +++ b/examples/edge-functions/supabase/functions/openai-image-generation/index.ts @@ -85,7 +85,7 @@ Deno.serve(async (req) => { // Upload the generated image to Supabase Storage const supabaseClient = createClient( Deno.env.get('SUPABASE_URL') || '', - Deno.env.get(SUPABASE_SECRET_KEYS['default']) || '' + SUPABASE_SECRET_KEYS['default'] || '' ) // Create a unique identifier for this generation diff --git a/examples/edge-functions/supabase/functions/read-storage/index.ts b/examples/edge-functions/supabase/functions/read-storage/index.ts index 443bf721a1548..d0063546b70d7 100644 --- a/examples/edge-functions/supabase/functions/read-storage/index.ts +++ b/examples/edge-functions/supabase/functions/read-storage/index.ts @@ -25,7 +25,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API publishable key - env var exported by default. - Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default']) ?? '', + SUPABASE_PUBLISHABLE_KEYS['default'] ?? '', // Create client with Auth context of the user that called the function. // This way your row-level-security (RLS) policies are applied. { diff --git a/examples/edge-functions/supabase/functions/restful-tasks/index.ts b/examples/edge-functions/supabase/functions/restful-tasks/index.ts index 2182334fc6a1f..0b4527abde80f 100644 --- a/examples/edge-functions/supabase/functions/restful-tasks/index.ts +++ b/examples/edge-functions/supabase/functions/restful-tasks/index.ts @@ -82,7 +82,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL') ?? '', // Supabase publishable key - env var exported by default. - Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default']) ?? '', + SUPABASE_PUBLISHABLE_KEYS['default'] ?? '', // Create client with Auth context of the user that called the function. // This way your row-level-security (RLS) policies are applied. { diff --git a/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts b/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts index a9badc940b136..c0c1e62b03cfe 100644 --- a/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts +++ b/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts @@ -23,7 +23,7 @@ Deno.serve(async (req: Request) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API PUBLISHABLE KEY - env var exported by default. - Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default']) ?? '', + SUPABASE_PUBLISHABLE_KEYS['default'] ?? '', // Create client with Auth context of the user that called the function. // This way your row-level-security (RLS) policies are applied. { diff --git a/examples/edge-functions/supabase/functions/sentry/index.ts b/examples/edge-functions/supabase/functions/sentry/index.ts index abdddd9b97d3f..23a04024a92cb 100644 --- a/examples/edge-functions/supabase/functions/sentry/index.ts +++ b/examples/edge-functions/supabase/functions/sentry/index.ts @@ -9,10 +9,7 @@ import * as Sentry from 'https://deno.land/x/sentry@7.102.0/index.mjs' const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) -const supabase = createClient( - Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! -) +const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default']!) Sentry.init({ dsn: Deno.env.get('SENTRY_DSN'), diff --git a/examples/edge-functions/supabase/functions/tweet-to-image/handler.tsx b/examples/edge-functions/supabase/functions/tweet-to-image/handler.tsx index ddd9c340c2bce..00e16086f49aa 100644 --- a/examples/edge-functions/supabase/functions/tweet-to-image/handler.tsx +++ b/examples/edge-functions/supabase/functions/tweet-to-image/handler.tsx @@ -70,7 +70,7 @@ export async function handler(req: Request) { // Supabase API URL - env var exported by default when deployed. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API SECRET KEY - env var exported by default when deployed. - Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + SUPABASE_SECRET_KEYS['default'] ?? '' ) // Upload image to storage. diff --git a/examples/edge-functions/supabase/functions/upstash-redis-ratelimit/index.ts b/examples/edge-functions/supabase/functions/upstash-redis-ratelimit/index.ts index ef5727c3b22a6..0c8a05e10ded1 100644 --- a/examples/edge-functions/supabase/functions/upstash-redis-ratelimit/index.ts +++ b/examples/edge-functions/supabase/functions/upstash-redis-ratelimit/index.ts @@ -12,7 +12,7 @@ Deno.serve(async (req) => { // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL') ?? '', // Supabase publishable key - env var exported by default. - Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default']) ?? '', + SUPABASE_PUBLISHABLE_KEYS['default'] ?? '', // Create client with Auth context of the user that called the function. // This way your row-level-security (RLS) policies are applied. { diff --git a/examples/prompts/edge-functions.md b/examples/prompts/edge-functions.md index ba48db3fa8e98..87447d7a99718 100644 --- a/examples/prompts/edge-functions.md +++ b/examples/prompts/edge-functions.md @@ -23,7 +23,7 @@ You're an expert in writing TypeScript and Deno JavaScript runtime. Generate **h - SUPABASE_SECRET_KEYS - SUPABASE_DB_URL -You then need to use `JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)` or `JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)` to access the actual keys in the code. For example, `Deno.env.get(SUPABASE_SECRET_KEYS['default'])` to access the default service key. 9. To set other environment variables (ie. secrets) users can put them in a env file and run the `supabase secrets set --env-file path/to/env-file` 10. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with `/function-name` so they are routed correctly. 11. File write operations are ONLY permitted on `/tmp` directory. You can use either Deno or Node File APIs. 12. Use `EdgeRuntime.waitUntil(promise)` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context. +You then need to parse them with `JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)` or `JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)` to access the actual keys in the code. For example, assign the parsed map first with `const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)` and then index it with `SUPABASE_SECRET_KEYS['default']` to access the default secret key. 9. To set other environment variables (ie. secrets) users can put them in a env file and run the `supabase secrets set --env-file path/to/env-file` 10. A single Edge Function can handle multiple routes. It is recommended to use a library like Express or Hono to handle the routes as it's easier for developer to understand and maintain. Each route must be prefixed with `/function-name` so they are routed correctly. 11. File write operations are ONLY permitted on `/tmp` directory. You can use either Deno or Node File APIs. 12. Use `EdgeRuntime.waitUntil(promise)` static method to run long-running tasks in the background without blocking response to a request. Do NOT assume it is available in the request / execution context. ## Example Templates diff --git a/examples/storage/protomaps/supabase/functions/maps-private/index.ts b/examples/storage/protomaps/supabase/functions/maps-private/index.ts index cacfc61de02c2..d0187cdafab66 100644 --- a/examples/storage/protomaps/supabase/functions/maps-private/index.ts +++ b/examples/storage/protomaps/supabase/functions/maps-private/index.ts @@ -27,6 +27,6 @@ Deno.serve((req) => { const { method, headers } = req // Add Auth header const modHeaders = new Headers(headers) - modHeaders.append('authorization', `Bearer ${Deno.env.get(SUPABASE_SECRET_KEYS['default'])!}`) + modHeaders.append('authorization', `Bearer ${SUPABASE_SECRET_KEYS['default']!}`) return fetch(url, { method, headers: modHeaders }) }) diff --git a/examples/storage/resumable-upload-signed-uppy/supabase/functions/create-upload-token/index.ts b/examples/storage/resumable-upload-signed-uppy/supabase/functions/create-upload-token/index.ts index 2d1fb040fed9a..b61f9fafc6bf2 100644 --- a/examples/storage/resumable-upload-signed-uppy/supabase/functions/create-upload-token/index.ts +++ b/examples/storage/resumable-upload-signed-uppy/supabase/functions/create-upload-token/index.ts @@ -5,7 +5,7 @@ const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) Deno.serve(async (req) => { const SUPABASE_URL = Deno.env.get('SUPABASE_URL') ?? '' - const SUPABASE_SECRET_KEY = Deno.env.get(SUPABASE_SECRET_KEYS['default']) ?? '' + const SUPABASE_SECRET_KEY = SUPABASE_SECRET_KEYS['default'] ?? '' const supabase = createClient(SUPABASE_URL, SUPABASE_SECRET_KEY) diff --git a/examples/user-management/expo-push-notifications/supabase/functions/push/index.ts b/examples/user-management/expo-push-notifications/supabase/functions/push/index.ts index 7f73cb91dedef..76ea8f56338a0 100644 --- a/examples/user-management/expo-push-notifications/supabase/functions/push/index.ts +++ b/examples/user-management/expo-push-notifications/supabase/functions/push/index.ts @@ -20,10 +20,7 @@ interface WebhookPayload { } const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!) -const supabase = createClient( - Deno.env.get('SUPABASE_URL')!, - Deno.env.get(SUPABASE_SECRET_KEYS['default'])! -) +const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default']!) Deno.serve(async (req) => { const payload: WebhookPayload = await req.json()