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/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/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)
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/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.
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)
}
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
.
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',
}
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)
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()