diff --git a/apps/docs/content/guides/functions/ai-models.mdx b/apps/docs/content/guides/functions/ai-models.mdx index debbbe102ef..f31c01ed897 100644 --- a/apps/docs/content/guides/functions/ai-models.mdx +++ b/apps/docs/content/guides/functions/ai-models.mdx @@ -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" ``` @@ -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" ``` @@ -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" ``` @@ -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" ``` diff --git a/apps/docs/content/guides/functions/deploy.mdx b/apps/docs/content/guides/functions/deploy.mdx index 525ee8a6d58..038d379281f 100644 --- a/apps/docs/content/guides/functions/deploy.mdx +++ b/apps/docs/content/guides/functions/deploy.mdx @@ -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 -``` - - - -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. - - - ## 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://.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', { -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. diff --git a/apps/docs/content/guides/functions/development-environment.mdx b/apps/docs/content/guides/functions/development-environment.mdx index e8384a07494..50b3c29fca8 100644 --- a/apps/docs/content/guides/functions/development-environment.mdx +++ b/apps/docs/content/guides/functions/development-environment.mdx @@ -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. - - - -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. - - - ### `supabase functions deploy hello-world` Deploy the function when you’re ready diff --git a/apps/docs/content/guides/functions/development-tips.mdx b/apps/docs/content/guides/functions/development-tips.mdx index eb4e34841bf..6a333357c42 100644 --- a/apps/docs/content/guides/functions/development-tips.mdx +++ b/apps/docs/content/guides/functions/development-tips.mdx @@ -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 diff --git a/apps/docs/content/guides/functions/ephemeral-storage.mdx b/apps/docs/content/guides/functions/ephemeral-storage.mdx index 0ae5c2aa58c..d3a58bff75e 100644 --- a/apps/docs/content/guides/functions/ephemeral-storage.mdx +++ b/apps/docs/content/guides/functions/ephemeral-storage.mdx @@ -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 }) diff --git a/apps/docs/content/guides/functions/examples/amazon-bedrock-image-generator.mdx b/apps/docs/content/guides/functions/examples/amazon-bedrock-image-generator.mdx index b9b3003f01e..99808e2f6b9 100644 --- a/apps/docs/content/guides/functions/examples/amazon-bedrock-image-generator.mdx +++ b/apps/docs/content/guides/functions/examples/amazon-bedrock-image-generator.mdx @@ -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: ' \ --header 'Content-Type: application/json' \ --data '{"prompt":"A beautiful picture of a bird"}' ``` diff --git a/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx b/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx index 6dbee26bdc9..6ea7ac92d9c 100644 --- a/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx +++ b/apps/docs/content/guides/functions/examples/elevenlabs-generate-speech-stream.mdx @@ -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'), diff --git a/apps/docs/content/guides/functions/examples/push-notifications.mdx b/apps/docs/content/guides/functions/examples/push-notifications.mdx index 827565b21a4..d36d99ef201 100644 --- a/apps/docs/content/guides/functions/examples/push-notifications.mdx +++ b/apps/docs/content/guides/functions/examples/push-notifications.mdx @@ -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) => { diff --git a/apps/docs/content/guides/functions/quickstart-dashboard.mdx b/apps/docs/content/guides/functions/quickstart-dashboard.mdx index 1a6cbf3b4c3..16ab71e2baf 100644 --- a/apps/docs/content/guides/functions/quickstart-dashboard.mdx +++ b/apps/docs/content/guides/functions/quickstart-dashboard.mdx @@ -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' }), diff --git a/apps/docs/content/guides/functions/recursive-functions.mdx b/apps/docs/content/guides/functions/recursive-functions.mdx index e77c2f8d83e..580225f4d74 100644 --- a/apps/docs/content/guides/functions/recursive-functions.mdx +++ b/apps/docs/content/guides/functions/recursive-functions.mdx @@ -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) => { ```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++) { diff --git a/apps/docs/content/guides/functions/schedule-functions.mdx b/apps/docs/content/guides/functions/schedule-functions.mdx index 254a37dad92..1d9a54dc14b 100644 --- a/apps/docs/content/guides/functions/schedule-functions.mdx +++ b/apps/docs/content/guides/functions/schedule-functions.mdx @@ -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; diff --git a/apps/docs/content/guides/functions/secrets.mdx b/apps/docs/content/guides/functions/secrets.mdx index c605ae6f969..00d09e0db91 100644 --- a/apps/docs/content/guides/functions/secrets.mdx +++ b/apps/docs/content/guides/functions/secrets.mdx @@ -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'] +) ``` --- diff --git a/apps/docs/content/guides/functions/storage-caching.mdx b/apps/docs/content/guides/functions/storage-caching.mdx index 592bc0c0914..ebb394a6558 100644 --- a/apps/docs/content/guides/functions/storage-caching.mdx +++ b/apps/docs/content/guides/functions/storage-caching.mdx @@ -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) => { -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! diff --git a/apps/docs/content/guides/functions/websockets.mdx b/apps/docs/content/guides/functions/websockets.mdx index d9ae931bc8a..96bbc615c37 100644 --- a/apps/docs/content/guides/functions/websockets.mdx +++ b/apps/docs/content/guides/functions/websockets.mdx @@ -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) => {