docs: Functions Key changes (#45224)

## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Documentation**
* Replaced legacy "Anon"/"Service Role" key terminology with
"Publishable Keys" and "Secret Keys" across Edge Functions guides
* Updated authentication examples and request headers (client-side vs
server-side) to reflect publishable/secret key usage
* Standardized environment-variable examples to use parsed secret-key
maps with a selectable default
* Removed guidance for bypassing JWT verification via the deprecated CLI
flag
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Kalleby Santos <kalleby_santos@hotmail.com>
This commit is contained in:
Chris ChinchillaandKalleby Santos authored and GitHub committed 2026-04-29 11:12:54 +00:00
1 parent 580598f0e8
commit b6dba956ef
14 files changed
+82 -79

No files matched your search

@@ -210,7 +210,7 @@ We are progressively rolling out support for the hosted solution. To sign up for
```bash
curl --get "http://localhost:54321/functions/v1/ollama-test" \
--data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
-H "Authorization: $ANON_KEY"
-H "apikey: $PUBLISHABLE_KEY"
```
</StepHikeCompact.Details>
</StepHikeCompact.Step>
@@ -311,7 +311,7 @@ Since Llamafile provides an OpenAI API compatible server, you can either use it
```bash
curl --get "http://localhost:54321/functions/v1/llamafile-test" \
--data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
-H "Authorization: $ANON_KEY"
-H "apikey: $PUBLISHABLE_KEY"
```
</StepHikeCompact.Details>
</StepHikeCompact.Step>
@@ -420,7 +420,7 @@ Since Llamafile provides an OpenAI API compatible server, you can either use it
```bash
curl --get "http://localhost:54321/functions/v1/llamafile-test" \
--data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
-H "Authorization: $ANON_KEY"
-H "apikey: $PUBLISHABLE_KEY"
```
</StepHikeCompact.Details>
</StepHikeCompact.Step>
@@ -464,7 +464,7 @@ Once the function is working locally, it's time to deploy to production.
```bash
curl --get "https://project-ref.supabase.co/functions/v1/ollama-test" \
--data-urlencode "prompt=write a short rap song about Supabase, the Postgres Developer platform, as sung by Nicki Minaj" \
-H "Authorization: $ANON_KEY"
-H "apikey: $PUBLISHABLE_KEY"
```
</StepHikeCompact.Details>
</StepHikeCompact.Step>
+3 -17
View File
@@ -64,20 +64,6 @@ Or deploy individual Edge Functions by specifying the function name:
supabase functions deploy hello-world
```
### Deploying public functions
By default, Edge Functions require a valid JWT in the authorization header. If you want to deploy Edge Functions without Authorization checks (commonly used for Stripe webhooks), you can pass the `--no-verify-jwt` flag:
```bash
supabase functions deploy hello-world --no-verify-jwt
```
<Admonition type="caution">
Be careful when using this flag, as it will allow anyone to invoke your Edge Function without a valid JWT. The Supabase client libraries automatically handle authorization.
</Admonition>
## Step 4: Verify successful deployment
🎉 Your function is now live!
@@ -88,13 +74,13 @@ When the deployment is successful, your function is automatically distributed to
## Step 5: Test your live function
You can now invoke your Edge Function using the project's `ANON_KEY`, which can be found in the [API settings](/dashboard/project/_/settings/api) of the Supabase Dashboard. You can invoke it from within your app:
You can now invoke your Edge Function using one of the project's `PUBLISHABLE_KEYS`, which can be found in the [API settings](/dashboard/project/_/settings/api) of the Supabase Dashboard. You can invoke it from within your app:
<$CodeTabs>
```bash name=cURL
curl --request POST 'https://<project_id>.supabase.co/functions/v1/hello-world' \
--header 'Authorization: Bearer ANON_KEY' \
--header 'apikey: PUBLISHABLE_KEY' \
--header 'Content-Type: application/json' \
--data '{ "name":"Functions" }'
```
@@ -114,7 +100,7 @@ const { data, error } = await supabase.functions.invoke('hello-world', {
<Admonition type="note">
Note that the `SUPABASE_PUBLISHABLE_KEY` is different in development and production. To get your production anon key, you can find it in your Supabase dashboard under Settings > API.
Note that the `SUPABASE_PUBLISHABLE_KEYS` is different in development and production. To get a publishable key, you can find it in your Supabase dashboard under Settings > API.
</Admonition>
@@ -78,8 +78,8 @@ It's recommended to organize your functions according to the following structure
├── functions
│ ├── import_map.json # Top-level import map
│ ├── _shared # Shared code (underscore prefix)
│ │ ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key
│ │ ├── supabaseClient.ts # Supabase client with ANON key
│ │ ├── supabaseAdmin.ts # Supabase client with SECRET key
│ │ ├── supabaseClient.ts # Supabase client with PUBLISHABLE key
│ │ └── cors.ts # Reusable CORS headers
│ ├── function-one # Use hyphens for function names
│ │ └── index.ts
@@ -113,16 +113,6 @@ Develop a specific function with hot reloading. Your functions run at `http://lo
Alternatively, use `supabase functions serve` to serve all functions at once.
### `supabase functions serve hello-world --no-verify-jwt`
If you want to serve an Edge Function without the default JWT verification. This is important for webhooks from Stripe, GitHub, etc. These services don't have your JWT tokens, so you need to skip auth verification.
<Admonition type="caution">
Be careful when disabling JWT verification, as it allows anyone to call your function, so only use it for functions that are meant to be publicly accessible.
</Admonition>
### `supabase functions deploy hello-world`
Deploy the function when you’re ready
@@ -7,16 +7,6 @@ subtitle: 'Tips for getting started with Edge Functions.'
Here are a few recommendations when you first start developing Edge Functions.
### Skipping authorization checks
By default, Edge Functions require a valid JWT in the authorization header. If you want to use Edge Functions without Authorization checks (commonly used for Stripe webhooks), you can pass the `--no-verify-jwt` flag when serving your Edge Functions locally.
```bash
supabase functions serve hello-world --no-verify-jwt
```
Be careful when using this flag, as it will allow anyone to invoke your Edge Function without a valid JWT. The Supabase client libraries automatically handle authorization.
### Using HTTP methods
Edge Functions support `GET`, `POST`, `PUT`, `PATCH`, `DELETE`, and `OPTIONS`. A Function can be designed to perform different actions based on a request's HTTP method. See the [example on building a RESTful service](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/restful-tasks) to learn how to handle different HTTP methods in your Function.
@@ -41,8 +31,8 @@ We recommend this folder structure:
├── functions
│ ├── import_map.json # A top-level import map to use across functions.
│ ├── _shared
│ │ ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key.
│ │ └── supabaseClient.ts # Supabase client with ANON key.
│ │ ├── supabaseAdmin.ts # Supabase client with SECRET key.
│ │ └── supabaseClient.ts # Supabase client with PUBLISHABLE key.
│ │ └── cors.ts # Reusable CORS headers.
│ ├── function-one # Use hyphens to name functions.
│ │ └── index.ts
@@ -89,10 +89,9 @@ You can write the zip file to ephemeral storage first, then use a background tas
import { BlobWriter, ZipReader } from 'https://deno.land/x/zipjs/index.js'
import { createClient } from 'jsr:@supabase/supabase-js@2'
const supabase = createClient(
Deno.env.get('SUPABASE_URL'),
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
)
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default'])
async function processZipFile(uploadId: string, filepath: string) {
const file = await Deno.open(filepath, { read: true })
@@ -53,6 +53,8 @@ import { decode } from 'npm:base64-arraybuffer'
console.log('Hello from Amazon Bedrock!')
const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
Deno.serve(async (req) => {
prepareVirtualFile('./aws/config')
prepareVirtualFile('./aws/credentials')
@@ -102,8 +104,9 @@ Deno.serve(async (req) => {
const supabaseClient = createClient(
// Supabase API URL - env var exported by default.
Deno.env.get('SUPABASE_URL')!,
// Supabase API ANON KEY - env var exported by default.
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
// Using the default Supabase API PUB KEY.
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_PUBLISHABLE_KEYS['default']
)
const { data: upload, error: uploadError } = await supabaseClient.storage
@@ -132,7 +135,7 @@ Deno.serve(async (req) => {
```bash
curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/amazon-bedrock' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' \
--header 'apikey: <SUPABASE_PUBLISHABLE_KEY>' \
--header 'Content-Type: application/json' \
--data '{"prompt":"A beautiful picture of a bird"}'
```
@@ -104,10 +104,10 @@ import { createClient } from 'npm:@supabase/supabase-js@2'
import { ElevenLabsClient } from 'npm:elevenlabs@1.52.0'
import * as hash from 'npm:object-hash'
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default'])
const client = new ElevenLabsClient({
apiKey: Deno.env.get('ELEVENLABS_API_KEY'),
@@ -71,9 +71,12 @@ Push notifications are an important part of any mobile app. They allow you to se
old_record: null | Notification
}
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
SUPABASE_SECRET_KEYS['default']
)
Deno.serve(async (req) => {
@@ -183,9 +186,12 @@ Push notifications are an important part of any mobile app. They allow you to se
schema: 'public'
}
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
SUPABASE_SECRET_KEYS['default']
)
Deno.serve(async (req) => {
@@ -132,8 +132,8 @@ https://YOUR_PROJECT_ID.supabase.co/functions/v1/hello-world
To invoke this Edge Function from within your application, you'll need API keys. Navigate to **Settings > API Keys** in your dashboard to find:
- **Anon Key** - For client-side requests (safe to use in browsers with RLS enabled)
- **Service Role Key** - For server-side requests (keep this secret! bypasses RLS)
- **Publishable Keys** - For client-side requests (safe to use in browsers with RLS enabled)
- **Secret Keys** - For server-side requests (keep this secret! bypasses RLS)
---
@@ -163,7 +163,7 @@ Now that your function is deployed, you can invoke it from within your app:
```jsx
import { createClient } from '@supabase/supabase-js'
const supabase = createClient('https://[YOUR_PROJECT_ID].supabase.co', 'YOUR_ANON_KEY')
const supabase = createClient('https://[YOUR_PROJECT_ID].supabase.co', 'YOUR_PUBLISHABLE_KEY')
const { data, error } = await supabase.functions.invoke('hello-world', {
body: { name: 'JavaScript' },
@@ -180,7 +180,7 @@ console.log(data) // { message: "Hello JavaScript!" }
const response = await fetch('https://[YOUR_PROJECT_ID].supabase.co/functions/v1/hello-world', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_ANON_KEY',
Authorization: 'Bearer YOUR_PUBLISHABLE_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({ name: 'Fetch' }),
@@ -44,7 +44,13 @@ When the rate limit is exceeded, calling another Edge Function throws a `RateLim
```typescript
import { createClient } from 'jsr:@supabase/supabase-js@2'
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_ANON_KEY')!)
const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_PUBLISHABLE_KEYS['default']
)
Deno.serve(async (req) => {
try {
@@ -82,12 +88,16 @@ Deno.serve(async (req) => {
<TabPanel id="fetch" label="fetch">
```typescript
const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
const SUPABASE_DEFAULT_PUBLISHABLE_KEY = SUPABASE_PUBLISHABLE_KEYS['default']
Deno.serve(async (req) => {
try {
const response = await fetch(`${Deno.env.get('SUPABASE_URL')}/functions/v1/other-function`, {
method: 'POST',
headers: {
Authorization: `Bearer ${Deno.env.get('SUPABASE_ANON_KEY')}`,
Authorization: `Bearer ${SUPABASE_DEFAULT_PUBLISHABLE_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ foo: 'bar' }),
@@ -130,7 +140,13 @@ You can also use `retryAfterMs` to implement automatic retries within your funct
```typescript
import { createClient } from 'jsr:@supabase/supabase-js@2'
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_ANON_KEY')!)
const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_PUBLISHABLE_KEYS['default']
)
async function invokeWithRetry(functionName: string, payload: object, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
@@ -27,11 +27,11 @@ To access the auth token securely for your Edge Function call, we recommend stor
### Invoke an Edge Function every minute
Store `project_url` and `anon_key` in Supabase Vault:
Store `project_url` and `publishable_key` in Supabase Vault:
```sql
select vault.create_secret('https://project-ref.supabase.co', 'project_url');
select vault.create_secret('YOUR_SUPABASE_ANON_KEY', 'anon_key');
select vault.create_secret('YOUR_SUPABASE_PUBLISHABLE_KEY', 'publishable_key');
```
Make a POST request to a Supabase Edge Function every minute:
@@ -47,7 +47,7 @@ select
url:= (select decrypted_secret from vault.decrypted_secrets where name = 'project_url') || '/functions/v1/function-name',
headers:=jsonb_build_object(
'Content-type', 'application/json',
'Authorization', 'Bearer ' || (select decrypted_secret from vault.decrypted_secrets where name = 'anon_key')
'Authorization', 'Bearer ' || (select decrypted_secret from vault.decrypted_secrets where name = 'publishable_key')
),
body:=concat('{"time": "', now(), '"}')::jsonb
) as request_id;
+11 -5
View File
@@ -42,15 +42,21 @@ For example, in a function:
import { createClient } from 'npm:@supabase/supabase-js@2'
const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// If you want to use a different api key, change 'default' to your preferred key name
// For user-facing operations (respects RLS)
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_PUBLISHABLE_KEYS['default'])
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_PUBLISHABLE_KEYS['default']
)
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
// For admin operations (bypasses RLS)
const supabaseAdmin = createClient(Deno.env.get('SUPABASE_URL')!, SUPABASE_SECRET_KEYS['default'])
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL')!,
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_SECRET_KEYS['default']
)
```
---
@@ -15,15 +15,18 @@ Edge Functions work seamlessly with [Supabase Storage](/docs/guides/storage). Th
## Basic file operations
Use the Supabase client to upload files directly from your Edge Functions. You'll need the service role key for server-side storage operations:
Use the Supabase client to upload files directly from your Edge Functions. You'll need the secret key for server-side storage operations:
```typescript
import { createClient } from 'npm:@supabase/supabase-js@2'
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
Deno.serve(async (req) => {
const supabaseAdmin = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY') ?? ''
Deno.env.get('SUPABASE_URL')!,
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_SECRET_KEYS['default']
)
// Generate your content
@@ -48,7 +51,7 @@ Deno.serve(async (req) => {
<Admonition type="caution">
Always use the `SUPABASE_SERVICE_ROLE_KEY` for server-side operations. Never expose this key in client-side code!
Always use one of the `SUPABASE_SECRET_KEYS` for server-side operations. Never expose this key in client-side code!
</Admonition>
@@ -135,9 +135,11 @@ To authenticate the user making WebSocket requests, you can pass the JWT in URL
```ts
import { createClient } from 'npm:@supabase/supabase-js@2'
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
const supabase = createClient(
Deno.env.get('SUPABASE_URL'),
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_SECRET_KEYS['default']
)
Deno.serve((req) => {
@@ -188,9 +190,11 @@ Deno.serve((req) => {
```ts
import { createClient } from 'npm:@supabase/supabase-js@2'
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
const supabase = createClient(
Deno.env.get('SUPABASE_URL'),
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
// If you want to use a different api key, change 'default' to your preferred key name
SUPABASE_SECRET_KEYS['default']
)
Deno.serve((req) => {