From 1e4e0254c51134ae4a9cd07b2d7637b7818ce86f Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Mon, 6 Nov 2023 20:44:52 +0100 Subject: [PATCH] docs: Edge Function docs (#18738) * docs: Updating the Edge Function docs - making them more accessible * fixing regional docs * heading * some integration guides * cleans up the import maps guide * cleans up the testing doc * Actions * cleaning up the error docs * easier quick start * fix spelling * changing the page title * Update apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update apps/docs/pages/guides/functions/regional-invocation.mdx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * remove extraneous comma * terry feedback * Update apps/docs/pages/guides/functions/auth.mdx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Update apps/docs/pages/guides/functions/auth.mdx Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> --------- Co-authored-by: Charis <26616127+charislam@users.noreply.github.com> --- .../NavigationMenu.constants.ts | 94 +++--- apps/docs/pages/guides/functions/auth.mdx | 112 ++++--- .../pages/guides/functions/cicd-workflow.mdx | 24 +- .../guides/functions/connect-to-postgres.mdx | 64 +++- .../docs/pages/guides/functions/debugging.mdx | 82 ++++- apps/docs/pages/guides/functions/deploy.mdx | 101 +++++++ .../pages/guides/functions/import-maps.mdx | 71 +++-- .../guides/functions/local-development.mdx | 88 +++--- .../pages/guides/functions/quickstart.mdx | 280 +++++++----------- .../guides/functions/regional-invocation.mdx | 35 ++- apps/docs/pages/guides/functions/secrets.mdx | 25 +- .../guides/functions/troubleshooting.mdx | 64 ---- .../guides/functions/typescript-support.mdx | 54 ---- .../docs/pages/guides/functions/unit-test.mdx | 32 +- apps/www/lib/redirects.js | 10 + 15 files changed, 613 insertions(+), 523 deletions(-) create mode 100644 apps/docs/pages/guides/functions/deploy.mdx delete mode 100644 apps/docs/pages/guides/functions/troubleshooting.mdx delete mode 100644 apps/docs/pages/guides/functions/typescript-support.mdx diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 558002501b6..c71089049f5 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -826,38 +826,69 @@ export const functions: NavMenuConstant = { }, { name: 'Quickstart', - url: '/guides/functions/quickstart', - }, - { - name: 'Features', url: undefined, items: [ - { name: 'TypeScript Support', url: '/guides/functions/typescript-support' }, - { name: 'Debugging Edge Functions', url: '/guides/functions/debugging' }, - { name: 'Managing packages using Import Maps', url: '/guides/functions/import-maps' }, + { + name: 'Getting started', + url: '/guides/functions/quickstart', + }, + { + name: 'Deploy to Production', + url: '/guides/functions/deploy', + }, + { name: 'Setting up your editor', url: '/guides/functions/local-development' }, ], }, { name: 'Guides', url: undefined, items: [ - { name: 'Developing Functions locally', url: '/guides/functions/local-development' }, - { name: 'Deploying with GitHub', url: '/guides/functions/cicd-workflow' }, - { name: 'Managing Secrets and Environment Variables', url: '/guides/functions/secrets' }, - { name: 'Integrating With Supabase Auth', url: '/guides/functions/auth' }, + { name: 'Managing dependencies', url: '/guides/functions/import-maps' }, + { name: 'Managing environment variables', url: '/guides/functions/secrets' }, + { name: 'Integrating with Supabase Auth', url: '/guides/functions/auth' }, + { + name: 'Integrating with Postgres', + url: '/guides/functions/connect-to-postgres', + }, { name: 'Integrating with Supabase Storage', url: '/guides/functions/storage-caching', }, - { name: 'CORS support for Invoking from the browser', url: '/guides/functions/cors' }, - { name: 'Scheduling Functions', url: '/guides/functions/schedule-functions' }, - { - name: 'Connecting directly to Postgres', - url: '/guides/functions/connect-to-postgres', - }, + { name: 'Regional invocations', url: '/guides/functions/regional-invocation' }, + { name: 'Deploying with GitHub Actions', url: '/guides/functions/cicd-workflow' }, + ], + }, + { + name: 'Debugging', + url: undefined, + items: [ + { name: 'Debugging Edge Functions', url: '/guides/functions/debugging' }, { name: 'Testing your Edge Functions', url: '/guides/functions/unit-test' }, - { name: 'Regional Invocation', url: '/guides/functions/regional-invocation' }, - { name: 'Troubleshooting', url: '/guides/functions/troubleshooting' }, + ], + }, + { + name: 'Examples', + url: '/guides/functions/examples', + items: [ + { name: 'CORS support for invoking from the browser', url: '/guides/functions/cors' }, + { name: 'Scheduling Functions', url: '/guides/functions/schedule-functions' }, + { name: 'Generating OG images ', url: '/guides/functions/examples/og-image' }, + { + name: 'CAPTCHA support with Cloudflare Turnstile', + url: '/guides/functions/examples/cloudflare-turnstile', + }, + { name: 'Building a Discord Bot', url: '/guides/functions/examples/discord-bot' }, + { name: 'Building a Telegram Bot', url: '/guides/functions/examples/telegram-bot' }, + { name: 'Handling Stripe Webhooks ', url: '/guides/functions/examples/stripe-webhooks' }, + { name: 'Rate-limiting with Redis', url: '/guides/functions/examples/rate-limiting' }, + { + name: 'Taking Screenshots with Puppeteer', + url: '/guides/functions/examples/screenshots', + }, + { + name: 'Slack Bot responding to mentions', + url: '/guides/functions/examples/slack-bot-mention', + }, ], }, { @@ -873,31 +904,6 @@ export const functions: NavMenuConstant = { { name: 'Type-Safe SQL with Kysely', url: '/guides/functions/kysely-postgres' }, ], }, - { - name: 'Examples', - url: '/guides/functions/examples', - items: [ - { name: 'Generating OpenAI GPT3 completions', url: '/guides/ai/examples/openai' }, - { name: 'Generating OG images ', url: '/guides/functions/examples/og-image' }, - { - name: 'CAPTCHA support with Cloudflare Turnstile', - url: '/guides/functions/examples/cloudflare-turnstile', - }, - { name: 'Building a Discord Bot', url: '/guides/functions/examples/discord-bot' }, - { name: 'Building a Telegram Bot', url: '/guides/functions/examples/telegram-bot' }, - { name: 'Handling Stripe Webhooks ', url: '/guides/functions/examples/stripe-webhooks' }, - { name: 'Integrating with Upstash Redis', url: '/guides/functions/examples/upstash-redis' }, - { name: 'Rate Limiting Edge Functions', url: '/guides/functions/examples/rate-limiting' }, - { - name: 'Taking Screenshots with Puppeteer', - url: '/guides/functions/examples/screenshots', - }, - { - name: 'Slack Bot responding to mentions', - url: '/guides/functions/examples/slack-bot-mention', - }, - ], - }, ], } diff --git a/apps/docs/pages/guides/functions/auth.mdx b/apps/docs/pages/guides/functions/auth.mdx index 599beee9484..6afaded5159 100644 --- a/apps/docs/pages/guides/functions/auth.mdx +++ b/apps/docs/pages/guides/functions/auth.mdx @@ -4,60 +4,88 @@ export const meta = { id: 'auth', title: 'Integrating With Supabase Auth', description: 'Supabase Edge Functions and Auth.', + subtitle: 'Supabase Edge Functions and Auth.', } -Edge Functions work seamlessly with [Supabase Auth](/docs/guides/auth), allowing you to identify which user called your function. +Edge Functions work seamlessly with [Supabase Auth](/docs/guides/auth). -When [invoking](/docs/reference/javascript/invoke) a function with one of the [client libraries](/docs/reference), the logged in user's JWT is automatically attached to the function call and becomes accessible within your function. +## Auth Context -This is important, for example, to identify which customer's credit card should be charged. You can see this concept end-to-end in our [Stripe example app](https://github.com/supabase-community/expo-stripe-payments-with-supabase-functions). +When a user makes a request to an Edge Function, you can use the Authorization header to set the Auth context in the Supabase client: -## Auth Context & RLS - -By creating a supabase client with the auth context from the function, you can do two things: - -1. Get the user object. -2. Run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/auth/row-level-security) policies enforced. - -```js mark=14,17:19,22:23 supabase/functions/select-from-table-with-auth-rls/index.ts -import { serve } from 'https://deno.land/std@0.177.0/http/server.ts' +```js mark=10 import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' -serve(async (req: Request) => { - try { - // Create a Supabase client with the Auth context of the logged in user. - 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_ANON_KEY') ?? '', - // Create client with Auth context of the user that called the function. - // This way your row-level-security (RLS) policies are applied. - { global: { headers: { Authorization: req.headers.get('Authorization')! } } } - ) - // Now we can get the session or user object - const { - data: { user }, - } = await supabaseClient.auth.getUser() +Deno.serve(async (req: Request) => { - // And we can run queries in the context of our authenticated user - const { data, error } = await supabaseClient.from('users').select('*') - if (error) throw error + const authHeader = req.headers.get('Authorization')! + const supabaseClient = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { global: { headers: { Authorization: authHeader } } } + ) - return new Response(JSON.stringify({ user, data }), { - headers: { 'Content-Type': 'application/json' }, - status: 200, - }) - } catch (error) { - return new Response(JSON.stringify({ error: error.message }), { - headers: { 'Content-Type': 'application/json' }, - status: 400, - }) - } }) ``` -See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts). +Importantly, this is done _inside_ the `Deno.serve()` callback argument, so that the Authorization header is set for each request. + +## Fetching the user + +After initializing a Supabase client with the Auth context, you can use `getUser()` to fetch the user object, and run queries in the context of the user with [Row Level Security (RLS)](/docs/guides/auth/row-level-security) policies enforced. + +```js mark=13:14 +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +Deno.serve(async (req: Request) => { + + const supabaseClient = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { global: { headers: { Authorization: req.headers.get('Authorization')! } } } + ) + + // Get the session or user object + const { data } = await supabaseClient.auth.getUser() + const user = data.user + + return new Response(JSON.stringify({ user }), { + headers: { 'Content-Type': 'application/json' }, + status: 200, + }) + +}) +``` + +## Row Level Security + +After initializing a Supabase client with the Auth context, all queries will be executed with the context of the user. For database queries, this means [Row Level Security](/docs/guides/auth/row-level-security) will be enforced. + +```js mark=13 +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' + +Deno.serve(async (req: Request) => { + + const supabaseClient = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { global: { headers: { Authorization: req.headers.get('Authorization')! } } } + ) + + // Database queries will have RLS policies enforced + const { data, error } = await supabaseClient.from('profiles').select('*') + + return new Response(JSON.stringify({ data }), { + headers: { 'Content-Type': 'application/json' }, + status: 200, + }) + +}) +``` + +## Example code + +See a full [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/select-from-table-with-auth-rls/index.ts). export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/functions/cicd-workflow.mdx b/apps/docs/pages/guides/functions/cicd-workflow.mdx index b729044c17e..9c37a254ada 100644 --- a/apps/docs/pages/guides/functions/cicd-workflow.mdx +++ b/apps/docs/pages/guides/functions/cicd-workflow.mdx @@ -3,11 +3,16 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'cicd-workflow', title: 'Deploying with GitHub Actions', - description: 'How to deploy Supabase Edge Functions with a CI / CD pipeline.', - video: 'https://www.youtube.com/v/6OMVWiiycLs', + description: 'Use GitHub Actions as a CI / CD pipeline to deploy your Edge Functions.', + subtitle: 'Use GitHub Actions as a CI / CD pipeline to deploy your Edge Functions.', + tocVideo: '6OMVWiiycLs', } -As described in the Supabase CLI [Environments Guide](/docs/guides/cli/managing-environments), you can use the [`setup-cli` GitHub Action](https://github.com/marketplace/actions/supabase-cli-action) to run Supabase CLI commands in your GitHub Actions, for example to deploy a Supabase Edge Function: +You can use the official [`setup-cli` GitHub Action](https://github.com/marketplace/actions/supabase-cli-action) to run Supabase CLI commands in your GitHub Actions. + +## Sample Workflow + +The following GitHub Action deploys all Edge Functions any time code is merged into the `main` branch: ```yaml name: Deploy Function @@ -36,7 +41,7 @@ jobs: - run: supabase functions deploy --project-ref $PROJECT_ID ``` -Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. +## Declarative Configuration Individual function configuration like [JWT verification](/docs/guides/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/guides/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. @@ -45,16 +50,9 @@ Individual function configuration like [JWT verification](/docs/guides/cli/confi verify_jwt = false ``` -
- -
+## Resources -See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/.github/workflows/deploy.yaml). +- See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/.github/workflows/deploy.yaml). export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/functions/connect-to-postgres.mdx b/apps/docs/pages/guides/functions/connect-to-postgres.mdx index 174a745220f..070c9e60a95 100644 --- a/apps/docs/pages/guides/functions/connect-to-postgres.mdx +++ b/apps/docs/pages/guides/functions/connect-to-postgres.mdx @@ -4,19 +4,49 @@ export const meta = { id: 'examples-postgres-on-the-edge', title: 'Connecting directly to Postgres', description: 'Connecting to Postgres from Edge Functions.', - video: 'https://www.youtube.com/v/cl7EuF1-RsY', + subtitle: 'Connecting to Postgres from Edge Functions.', + tocVideo: 'cl7EuF1-RsY', } -
- -
+Connect to your Postgres database from an Edge Function by using the `supabase-js` client. +You can also substitute any other Postgres client. -Supabase Edge Functions allow you to go beyond HTTP and can connect to your Postgres Database directly! +## Using supabase-js + +The `supabase-js` client is a great option for connecting to your Supabase database since it handles authorization with Row Level Security, and it automatically formats your response as JSON. + +```ts index.ts +import { createClient } from 'https://esm.sh/@supabase/supabase-js' + +Deno.serve(async (_req) => { + try { + const supabase = createClient( + Deno.env.get('SUPABASE_URL') ?? '', + Deno.env.get('SUPABASE_ANON_KEY') ?? '', + { global: { headers: { Authorization: req.headers.get('Authorization')! } } } + ) + + const { data, error } = await supabase.from('countries').select('*') + + if (error) { + throw error + } + + return new Response(JSON.stringify({ data }), { + headers: { 'Content-Type': 'application/json' }, + status: 200, + }) + } catch (err) { + return new Response(String(err?.message ?? err), { status: 500 }) + } +}) +``` + +## Using a Postgres client + +Because Edge Functions are a server-side technology, it's safe to connect directly to your database using any popular Postgres client. This means you can run raw SQL from your Edge Functions. + +[`deno-postgres`](https://deno.land/x/postgres) is a lightweight PostgreSQL driver for Deno. Check out our [example](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/postgres-on-the-edge) for the full code. ```ts index.ts import * as postgres from 'https://deno.land/x/postgres@v0.17.0/mod.ts' @@ -37,7 +67,6 @@ serve(async (_req) => { // Run a query const result = await connection.queryObject`SELECT * FROM animals` const animals = result.rows // [{ id: 1, name: "Lion" }, ...] - console.log(animals) // Encode the result as pretty printed JSON const body = JSON.stringify( @@ -49,9 +78,7 @@ serve(async (_req) => { // Return the response with the correct content type header return new Response(body, { status: 200, - headers: { - 'Content-Type': 'application/json; charset=utf-8', - }, + headers: { 'Content-Type': 'application/json; charset=utf-8' }, }) } finally { // Release the connection back into the pool @@ -64,6 +91,15 @@ serve(async (_req) => { }) ``` +
+ +
+ export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/functions/debugging.mdx b/apps/docs/pages/guides/functions/debugging.mdx index 6b1d46d1eda..09d3b473e96 100644 --- a/apps/docs/pages/guides/functions/debugging.mdx +++ b/apps/docs/pages/guides/functions/debugging.mdx @@ -3,9 +3,16 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'functions-debugging', title: 'Debugging Edge Functions', - description: 'Debug Edge Functions in production.', + description: 'Debugging tips and Edge Function limitiations.', + subtitle: 'Debugging tips and Edge Function limitiations.', } +## Debugging + +Logs are provided for each function invocation, locally and in production. + +### Debugging in production + You can debug your deployed Edge Functions using the ["Functions" section](https://supabase.com/dashboard/project/_/functions) of the Dashboard. There are two debugging tools available: - Invocations: shows the Request and Response for each execution. @@ -13,8 +20,81 @@ You can debug your deployed Edge Functions using the ["Functions" section](https ![Function invocations.](/docs/img/guides/functions/function-logs.png) +### Debugging locally + When [developing locally](/docs/guides/functions/local-development) you will see error messages and console log statements printed to your local terminal window. +## Limitations + +### Deno Deploy limitations + +- Deno does not support outgoing connections to ports `25`, `465`, and `587`. +- Deno-deployed functions cannot read or write to the file system. +- NPM modules are not supported. + +### Supabase Edge Functions + +- Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`). + +## Troubleshooting + +### Unable to call Edge Function + +If you're unable to call your Edge Function or are experiencing any CORS issues: + +- Make sure you followed the [CORS guide](/docs/guides/functions/cors). +- Check your function logs. Navigate to the [Functions section in your dashboard](https://supabase.com/dashboard/project/_/functions), select your function from the list, and click `Logs`. Check for any errors. + +There are two debugging tools available: Invocations and Logs. Invocations shows the Request and Response for each execution, while Logs shows any platform events, including deployments and errors. + +### Unable to deploy Edge Function + +- Make sure you're on the latest version of the [Supabase CLI](/docs/guides/cli#updates). +- Run the deploy command with the `--debug` flag. +- Run the deploy command with the `--legacy-bundle` flag. +- If the output from the commands above does not help you to resolve the issue, open a support ticket via the Supabase Dashboard (by clicking the "Help" button at the top right) and include all output from the commands mentioned above. + +### Edge Function takes too long to respond + +- Navigate to the [Functions section in your Supabase dashboard](https://supabase.com/dashboard/project/_/functions), select your function from the list, and click `Logs`. +- In the logs, look for the `booted` event and check if they have consistent boot times. + - If the boot times are similar, it's likely an issue with your function's code, such as a large dependency. + - If only some of the `booted` events are slow, find the affected `region` in the metadata and submit a support request via the "Help" button at the top. + +### Issues serving Edge Functions locally with the Supabase CLI + +- Make sure you're on the latest version of the [Supabase CLI](/docs/guides/cli#updates). +- Run the serve command with the `--debug` flag. +- Support engineers can then try to run the provided sample code locally and see if they can reproduce the issue. +- Search the [Edge Runtime](https://github.com/supabase/edge-runtime) and [CLI](https://github.com/supabase/cli) repos for the error message, to see if it has been reported before. +- If the output from the commands above does not help you to resolve the issue, please open a support ticket via the Supabase Dashboard (by clicking the "Help" button at the top right) and include all output and details about your commands. + +## Advanced Techniques + +### Checking Function Boot Time + +Check the logs for the function. In the logs, look for a "Booted" event and note the reported boot time. If available, click on the event to access more details, including the regions from where the function was served. Investigate if the boot time is excessively high (longer than 1 second) and note any patterns or regions where it occurs. + +### Finding Bundle Size + +To find the bundle size of a function, run the following command locally: + +```bash +deno info /path/to/function/index.ts +``` + +Look for the "size" field in the output which represents the approximate bundle size of the function.You can find the accurate bundle size when you deploy your function via Supabase CLI. If the function is part of a larger application, consider examining the bundle size of the specific function independently. + +### Analyze Dependencies + +Run `deno info`, providing the path to your input map if you use one. +Review the dependencies listed in the output. Pay attention to any significantly large dependencies, as they can contribute to increased bundle size and potential boot time issues. +Examine if there are any unnecessary or redundant dependencies that can be removed. Check for outdated dependencies and update to the latest versions if possible. + +```bash +deno info --import-map=/path/to/import_map.json /path/to/function/index.ts +``` + export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/functions/deploy.mdx b/apps/docs/pages/guides/functions/deploy.mdx new file mode 100644 index 00000000000..37089898478 --- /dev/null +++ b/apps/docs/pages/guides/functions/deploy.mdx @@ -0,0 +1,101 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'functions-deploy', + title: 'Deploy to Production', + description: 'Deploy your Edge Functions to your remote Supabase Project.', + subtitle: 'Deploy your Edge Functions to your remote Supabase Project.', + tocVideo: 'rzglqRdZUQE', +} + +Once you have developed your Edge Functions locally, you can deploy them to your Supabase project. + +## Login to the CLI + +Log in to the Supabase CLI if necessary: + +```bash +supabase login +``` + + + +See the [CLI Docs](/docs/guides/cli) to learn how to install the Supabase CLI on your local machine. + + + +## Get your Project ID + +Get the project ID associated with your function by running: + +```bash +supabase projects list +``` + + + +If you haven't yet created a Supabase project, you can do so by visiting [database.new](https://database.new). + + + +## Link your local project + +[Link](/docs/reference/cli/usage#supabase-link) your local project to your remote Supabase project using the ID you just retrieved: + +```bash +supabase link --project-ref your-project-id +``` + +## Deploy your Edge Functions + +You can deploy all of your Edge Functions with a single command: + +```bash +supabase functions deploy +``` + +You can deploy individual Edge Functions by specifying the name of the function in the deploy command: + +```bash +supabase functions deploy hello-world +``` + +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 deploying your Edge Functions. + +```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. + +## Invoking remote Functions + +You can now invoke your Edge Function using the project's `ANON_KEY`, which can be found in the [API settings](https://supabase.com/dashboard/project/_/settings/api) of the Supabase Dashboard. + + + +```bash cURL +curl --request POST 'https://.supabase.co/functions/v1/hello-world' \ + --header 'Authorization: Bearer ANON_KEY' \ + --header 'Content-Type: application/json' \ + --data '{ "name":"Functions" }' +``` + +```js JavaScript +import { createClient } from '@supabase/supabase-js' + +// Create a single supabase client for interacting with your database +const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') + +const { data, error } = await supabase.functions.invoke('hello-world', { + body: { name: 'Functions' }, +}) +``` + + + +You should receive the response `{ "message":"Hello Functions!" }`. + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/import-maps.mdx b/apps/docs/pages/guides/functions/import-maps.mdx index f6c360dc3fd..d0916c076ca 100644 --- a/apps/docs/pages/guides/functions/import-maps.mdx +++ b/apps/docs/pages/guides/functions/import-maps.mdx @@ -2,33 +2,53 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'functions-import-maps', - title: 'Managing packages using Import Maps', - description: 'Managing packages using Import Maps.', - video: 'https://www.youtube.com/v/ILr3cneZuFk', + title: 'Managing dependencies', + description: 'Managing packages and dependencies.', + subtitle: 'Managing packages and dependencies.', + tocVideo: 'ILr3cneZuFk', } -
- -
+Developing with Edge Functions is similar to developing with Node.js, but with a few key differences. This guide will help you understand how to manage your dependencies. -Since [Supabase CLI](https://supabase.com/docs/guides/resources/supabase-cli) version `1.33.0` [import maps](https://deno.land/manual@v1.29.2/node/import_maps) can be used with Supabase Edge Functions. +## Importing dependencies -[Import maps](https://github.com/WICG/import-maps#the-import-map) is a web-platform standard that allows you to use bare specifiers with Deno without having to install the Node.js package locally. +Supabase Edge Functions support: -So if we want to do the following in our code: +- The Deno [standard library](https://deno.land/std) +- Third party [modules](https://deno.land/x) +- Node.js modules published to npm -```ts, ignore -import lodash from "lodash"; +We recommend using [esm.sh](https://esm.sh/) for importing modules that are published to npm. To do so, put `https://esm.sh/` in front of the package name. For example, import the `supabase-js` package like this: + +```ts +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' ``` -We can accomplish this using an import map, and we don't even have to install the `lodash` package locally. We would want to create a JSON file (for example **import_map.json**) with the following: + -```json +Full support for NPM is under development and coming soon. + + + +## Importing Types + +If your [environment is set up properly](/docs/guides/functions/local-development) and the module you're importing is exporting types, the import will have types and autocompletion support. + +## Using Import Maps + +An [Import Map](https://github.com/WICG/import-maps#the-import-map) is similar to a `package.json` file. They are a way to manage your dependencies. Consider this code: + +```ts +// without import maps: +import lodash from 'https://cdn.skypack.dev/lodash' + +// with import maps: +import lodash from 'lodash' +``` + +You can accomplish this using an Import Map, which is a JSON file with the following: + +```json supabase/functions/import_map.json { "imports": { "lodash": "https://cdn.skypack.dev/lodash" @@ -36,15 +56,22 @@ We can accomplish this using an import map, and we don't even have to install th } ``` -## Import Map Placement +We recommend creating one `import_map.json` within the `/supabase/functions` folder, similar to a `package.json` file, to define imports that can be used across all of your project's functions. -We recommend creating one `import_map.json` within the `/supabase/functions` folder (see [Organizing your Edge Functions](/docs/guides/functions/quickstart#organizing-your-edge-functions)), similar to a `package.json` file, to define imports that can be used across all of your project's functions. +```bash +└── supabase + ├── functions + │ ├── import_map.json # A top-level import map to use across functions.. + │ ├── function-one + │ │ └── index.ts + └── config.toml +``` Alternatively, you can create one `import_map.json` file in each function folder, which will take priority over a top-level file. -Lastly, you can override this default behaviour by providing the `--import-map ` flag to the `serve` and `deploy` commands. +You can override this default behavior by providing the `--import-map ` flag to the `serve` and `deploy` commands. -## Visual Studio Code Configuration +### Configuring VSCode In order for vscode to understand the imports correctly, you need to specify the `deno.importMap` flag in your `.vscode/settings.json` file: diff --git a/apps/docs/pages/guides/functions/local-development.mdx b/apps/docs/pages/guides/functions/local-development.mdx index 9cb55927208..6b1b6e51ffa 100644 --- a/apps/docs/pages/guides/functions/local-development.mdx +++ b/apps/docs/pages/guides/functions/local-development.mdx @@ -2,14 +2,45 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'functions-local-development', - title: 'Developing Functions locally', - description: 'Run your Edge Functions locally.', + title: 'Local development', + description: 'Setup local development environment for Edge Functions.', + subtitle: 'Setup local development environment for Edge Functions.', } -## Setting Up Your Environment +We recommend installing the Deno CLI and related tools for local development. + +## Deno Support You can follow the [Deno guide](https://deno.com/manual@v1.32.5/getting_started/setup_your_environment) for setting up your development environment with your favorite editor/IDE. +## Deno with Visual Studio Code + +When using VSCode, you should install both the Deno CLI and the the Deno language server [via this link](vscode:extension/denoland.vscode-deno) or by browsing the extensions in vscode and choosing to install the _Deno_ extension. + +## Deno support in subfolders + +You can enable the Deno language server for specific sub-paths in a workspace, while using VSCode's built-in JavaScript/TypeScript language server for all other files. + +For example if you have a project like this: + +``` +project +├── app +└── supabase + └── functions +``` + +To enable the Deno language server only for the `supabase/functions` folder, add `./supabase/functions` to the list of _Deno: Enable Paths_ in the configuration. In your `.vscode/settings.json` file add: + +```json +{ + "deno.enablePaths": ["./supabase/functions"], + "deno.importMap": "./supabase/functions/import_map.json" +} +``` + +## Multi-root workspaces in VS Code + When developing with VS Code inside of an existing application, you can utilize [multi-root workspaces](https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces).
@@ -50,57 +81,6 @@ For example, see this `edge-functions.code-workspace` configuration for a CRA (c } ``` -## Running Edge Functions locally - -You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve): - -```bash -supabase start # start the supabase stack -supabase functions serve # start the Functions watcher -``` - -The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server. - -## Invoking Edge Functions locally - -While serving your local Edge Function, you can invoke it using curl: - -```bash -curl --request POST 'http://localhost:54321/functions/v1/function-name' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ - --header 'Content-Type: application/json' \ - --data '{ "name":"Functions" }' -``` - -or using one of the [client libraries](/docs#reference-documentation), e.g. using [supabase-js](/docs/reference/javascript/functions-invoke): - -```js -import { createClient } from '@supabase/supabase-js' - -// Use the credentials outputted in your terminal when running `supabase start` -const supabase = createClient( - 'http://localhost:54321', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0' -) - -const { data, error } = await supabase.functions.invoke('function-name', { - body: { name: 'Functions' }, -}) -``` - -You should see the response `{ "message":"Hello Functions!" }`. - -If you execute the function with a different payload the response will change.
-Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again! - - - -- Edge Functions don't serve HTML content (`GET` requests that return `text/html` are rewritten to `text/plain`). -- The `Authorization` header is required. You can use either the `ANON` key, the `SERVICE_ROLE` key, or a logged-in user's JWT. -- The Function is proxied through the local API (`http://localhost:54321`) - - - export const Page = ({ children }) => export default Page diff --git a/apps/docs/pages/guides/functions/quickstart.mdx b/apps/docs/pages/guides/functions/quickstart.mdx index a28f5bf4adf..6d847ca8bcf 100644 --- a/apps/docs/pages/guides/functions/quickstart.mdx +++ b/apps/docs/pages/guides/functions/quickstart.mdx @@ -2,32 +2,27 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'functions-quickstart', - title: 'Edge Functions Quickstart', - description: 'Globally distributed TypeScript Functions.', - sidebar_label: 'Quickstart', - video: 'https://www.youtube.com/v/rzglqRdZUQE', + title: 'Developing Edge Functions locally', + description: 'Get started with Edge Functions on your local machine.', + subtitle: 'Get started with Edge Functions on your local machine.', + tocVideo: 'rzglqRdZUQE', } -Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes. +Let's create a basic Edge Function on your local machine and then invoke it using the Supabase CLI. -
- -
+## Initialize a project -## Prerequisites +Create a new Supabase project in a folder on your local machine: -Follow the steps to prepare your Supabase project on your local machine. +```bash +supabase init +``` -- Install the Supabase CLI. [Docs](/docs/guides/cli). -- Login to the CLI using the command: `supabase login`. [Docs](/docs/reference/cli/usage#supabase-login). -- Initialize Supabase inside your project using the command: `supabase init`. [Docs](/docs/guides/getting-started/local-development#getting-started). -- Link to your Remote Project using the command `supabase link --project-ref your-project-ref`. [Docs](/docs/reference/cli/usage#supabase-link). -- Setup your environment: Follow [the steps below](/docs/guides/functions/quickstart#setting-up-your-environment). + + +Check out the [CLI Docs](/docs/guides/cli) to learn how to install the Supabase CLI on your local machine. + + ## Create an Edge Function @@ -37,195 +32,99 @@ Let's create a new Edge Function called `hello-world` inside your project: supabase functions new hello-world ``` -This creates a function stub in your `supabase` folder at `./functions/hello-world/index.ts`. - -## Deploy to production - -### Deploy a specific function +This creates a function stub in your `supabase` folder: ```bash -supabase functions deploy hello-world +└── supabase + ├── functions + │ └── hello-world + │ │ └── index.ts ## Your function code + └── config.toml ``` -This command bundles your Edge Function from `./functions/hello-world/index.ts` and deploys it to the Supabase platform. -The command outputs a URL to the Supabase Dashboard which you can open to find view more details. Let's open the link to find the execution command. +## Running Edge Functions locally - - -By default, Edge Functions require a valid JWT in the authorization header. This header is automatically set when invoking your function via a Supabase client library. - -If you want to use Edge Functions to handle webhooks (e.g. [Stripe payment webhooks](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/stripe-webhooks) etc.), you need to pass the `--no-verify-jwt` flag when deploying your function. - - - -### Deploy all functions +You can run your Edge Function locally using [`supabase functions serve`](/docs/reference/cli/usage#supabase-functions-serve): ```bash -supabase functions deploy +supabase start # start the supabase stack +supabase functions serve # start the Functions watcher ``` -Since Supabase CLI [v1.62.0](https://github.com/supabase/cli/releases/tag/v1.62.0) you can deploy all functions with a single command. This is useful for example when [deploying with GitHub Actions](/docs/guides/functions/cicd-workflow). +The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server. -Individual function configuration like [JWT verification](/docs/guides/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/guides/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. +## Invoking Edge Functions locally -```toml -[functions.hello-world] -verify_jwt = false -``` +While serving your local Edge Function, you can invoke it using curl or one of the client libraries: -## Invoking remote functions + -You can invoke Edge Functions using curl: - -```bash -curl --request POST 'https://.supabase.co/functions/v1/hello-world' \ - --header 'Authorization: Bearer ANON_KEY' \ +```bash cURL +curl --request POST 'http://localhost:54321/functions/v1/hello-world' \ + --header 'Authorization: Bearer SUPABASE_ANON_KEY' \ --header 'Content-Type: application/json' \ --data '{ "name":"Functions" }' ``` - - -If you receive an error `Invalid JWT`, find the `ANON_KEY` of your project in the Dashboard under `Settings > API`. - - - -or using one of the [client libraries](/docs#reference-documentation), e.g. using [supabase-js](/docs/reference/javascript/functions-invoke): - -```js -// https://supabase.com/docs/reference/javascript/installing +```js JavaScript import { createClient } from '@supabase/supabase-js' -// Create a single supabase client for interacting with your database -const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') +const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY) const { data, error } = await supabase.functions.invoke('hello-world', { body: { name: 'Functions' }, }) ``` -After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`. + -### Error Handling + -When interacting with Edge Functions, it's important to be prepared for potential errors that might occur during the invocation. To handle errors effectively, the following error handling code can be used: +Run `supabase status` to see your local credentials. -```js -import { FunctionsHttpError, FunctionsRelayError, FunctionsFetchError } from '@supabase/supabase-js' + -const { data, error } = await supabase.functions.invoke('hello', { - headers: { - 'my-custom-header': 'my-custom-header-value', - }, - body: { foo: 'bar' }, -}) +You should see the response `{ "message":"Hello Functions!" }`. -if (error instanceof FunctionsHttpError) { - const errorMessage = await error.context.json() - console.log('Function returned an error', errorMessage) -} else if (error instanceof FunctionsRelayError) { - console.log('Relay error:', error.message) -} else if (error instanceof FunctionsFetchError) { - console.log('Fetch error:', error.message) -} +If you execute the function with a different payload, the response will change. + +Modify the `--data '{"name":"Functions"}'` line to `--data '{"name":"World"}'` and try invoking the command again. + +## Next steps + +Check out the [Deploy to Production](/docs/guides/functions/deploy) guide to make your Edge Function available to the world. + +Read on for some common development tips. + +## Development tips + +Here are a few recommendations when 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 ``` -In the provided error handling code, we're checking the type of error that occurred using the "instanceof" operator. This helps us differentiate between different types of errors and handle them appropriately. Depending on the type of error, we either log the error message or information specific to that type of error. +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. -This error handling mechanism ensures that your application can gracefully handle errors that might arise during the execution of Edge Functions, providing a more robust user experience. +### Using HTTP Methods -## Importing Node npm modules +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. -We recommend using [esm.sh](https://esm.sh/) for importing Node.js modules that are published to npm. To do so you simply put `https://esm.sh/` in front of the package name. + -For example, when you want to use supabase-js within Supabase Edge Functions, you would import `createClient` as follows: +HTML content is not supported. `GET` requests that return `text/html` will be rewritten to `text/plain`. -```ts -import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' -``` + -As long as your environment is set up properly and the module you're importing is exporting types, the import will have types and autocompletion support. +### Naming Edge Functions -## Setting Up Your Environment +We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase). -You can follow the [Deno guide](https://deno.com/manual@v1.32.5/getting_started/setup_your_environment) for setting up your development environment with your favorite editor/IDE. - -### Deno with Visual Studio Code (vscode) - -Install the Deno language server [via this link](vscode:extension/denoland.vscode-deno) or by browsing the extensions in vscode and choosing to install the _Deno_ extension. - -#### Partially Deno enabling a workspace - -In a given workspace (or workspace folder), sub-paths can be enabled for Deno, while code outside those paths will be not be enabled and the vscode built-in JavaScript/TypeScript language server will be used. - -For example if you have a project like this: - -``` -project -├── app -└── supabase - └── functions -``` - -Where you only want to enabled the `supabase/functions` path (and its subpaths) to be Deno enabled, you will want to add `./supabase/functions` to the list of _Deno: Enable Paths_ in the configuration. In your `.vscode/settings.json` file add: - -```json -{ - "deno.enablePaths": ["./supabase/functions"], - "deno.importMap": "./supabase/functions/import_map.json" -} -``` - -#### Multi-root workspaces - -Alternatively, you can utilize [multi-root workspaces](https://code.visualstudio.com/docs/editor/workspaces#_multiroot-workspaces). - -
- -
- -For example, see this `edge-functions.code-workspace` configuration for a CRA (create react app) client with Supabase Edge Functions. You can find the complete example on [GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions). - -```json -{ - "folders": [ - { - "name": "project-root", - "path": "./" - }, - { - "name": "client", - "path": "app" - }, - { - "name": "supabase-functions", - "path": "supabase/functions" - } - ], - "settings": { - "files.exclude": { - "node_modules/": true, - "app/": true, - "supabase/functions/": true - }, - "deno.importMap": "./supabase/functions/import_map.json" - } -} -``` - -## Database Functions vs Edge Functions - -For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database -and can be called remotely using the [REST and GraphQL API](/docs/guides/api). - -For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript. - -## Organizing your Edge Functions +### Organizing your Edge Functions We recommend developing “fat functions”. This means that you should develop few large functions, rather than many small functions. One common pattern when developing Functions is that you need to share code between two or more Functions. To do this, you can store any shared code in a folder prefixed with an underscore (`_`). We also recommend a separate folder for [Unit Tests](/docs/guides/functions/unit-test) including the name of the function followed by a `-test` suffix. We recommend this folder structure: @@ -249,22 +148,43 @@ We recommend this folder structure: └── config.toml ``` -## Naming Edge Functions +### Using config.toml -We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase). +Individual function configuration like [JWT verification](/docs/guides/cli/config#functions.function_name.verify_jwt) and [import map location](/docs/guides/cli/config#functions.function_name.import_map) can be set via the `config.toml` file. -## Using HTTP Methods +```toml supabase/config.toml +[functions.hello-world] +verify_jwt = false +import_map = './import_map.json' +``` -Edge Functions supports `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. +### Error Handling -## Limitations +The `supabase-js` library provides several error types that you can use to handle errors that might occur when invoking Edge Functions: -- Deno Deploy limitations - - Deno does not support outgoing connections to ports `25`, `465`, and `587`. - - Cannot read or write to File System - - NPM modules are not supported. -- Edge Functions - - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`). +```js +import { FunctionsHttpError, FunctionsRelayError, FunctionsFetchError } from '@supabase/supabase-js' + +const { data, error } = await supabase.functions.invoke('hello', { + headers: { 'my-custom-header': 'my-custom-header-value' }, + body: { foo: 'bar' }, +}) + +if (error instanceof FunctionsHttpError) { + const errorMessage = await error.context.json() + console.log('Function returned an error', errorMessage) +} else if (error instanceof FunctionsRelayError) { + console.log('Relay error:', error.message) +} else if (error instanceof FunctionsFetchError) { + console.log('Fetch error:', error.message) +} +``` + +### Database Functions vs Edge Functions + +For data-intensive operations we recommend using [Database Functions](/docs/guides/database/functions), which are executed within your database and can be called remotely using the [REST and GraphQL API](/docs/guides/api). + +For use-cases which require low-latency we recommend [Edge Functions](/docs/guides/functions), which are globally-distributed and can be written in TypeScript. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/functions/regional-invocation.mdx b/apps/docs/pages/guides/functions/regional-invocation.mdx index 994549bd9e4..2272a547d0d 100644 --- a/apps/docs/pages/guides/functions/regional-invocation.mdx +++ b/apps/docs/pages/guides/functions/regional-invocation.mdx @@ -2,26 +2,28 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'function-regional-invocation', - title: 'Regional Invocation', + title: 'Regional Invocations', description: 'How to execute an Edge Functions in a particular region.', + subtitle: 'How to execute an Edge Function in a particular region.', } -By default, Edge Functions are executed in the region closest to the user making the request. This helps to reduce network latency and provide faster responses to the user. +Edge Functions are executed in the region closest to the user making the request. This helps to reduce network latency and provide faster responses to the user. -However, if your function does lot of database or storage operations, invoking the function in the same region as your DB may provide better performance. - -Some situations where this might be helpful include: +However, if your Function performs lots of database or storage operations, invoking the Function in the same region as your database may provide better performance. Some situations where this might be helpful include: - Bulk adding and editing records in your database - Uploading files -If you prefer to do this, you can specify the region when invoking the function. +Supabase provides an option to specify the region when invoking the Function. -### How to set the region via HTTP headers +## Using the `x-region` header -Use the `x-region` HTTP header when calling an Edge Function to determine where the Function should be executed. +Use the `x-region` HTTP header when calling an Edge Function to determine where the Function should be executed: -```bash + + +```bash cURL +# https://supabase.com/docs/guides/functions/deploy#invoking-remote-functions curl --request POST 'https://.supabase.co/functions/v1/hello-world' \ --header 'Authorization: Bearer ANON_KEY' \ --header 'Content-Type: application/json' \ @@ -29,24 +31,25 @@ curl --request POST 'https://.supabase.co/functions/v1/hello-world' --data '{ "name":"Functions" }' ``` -You can also specify the region using the [client libraries](/docs#reference-documentation), such as [supabase-js](/docs/reference/javascript/functions-invoke): - -```js +```js JavaScript // https://supabase.com/docs/reference/javascript/installing import { createClient } from '@supabase/supabase-js' // Create a single supabase client for interacting with your database const supabase = createClient('https://xyzcompany.supabase.co', 'public-anon-key') +// https://supabase.com/docs/reference/javascript/functions-invoke const { data, error } = await supabase.functions.invoke('hello-world', { body: { name: 'Functions' }, headers: { 'x-region': 'eu-west-3' }, }) ``` -You can verify the region the function was executed by looking at the `x-sb-edge-region` HTTP header in the response. You can also find it as metadata in [Edge Function Logs](/docs/guides/functions/debugging) + -### Supported regions +You can verify the execution region by looking at the `x-sb-edge-region` HTTP header in the response. You can also find it as metadata in [Edge Function Logs](/docs/guides/functions/debugging). + +## Available regions These are the currently supported region values you can provide for `x-region` header. @@ -65,9 +68,9 @@ These are the currently supported region values you can provide for `x-region` h - `us-west-1` - `us-west-2` -### What happens if the region has an outage? +## Handling regional outages -If you explicitly specify the region via `x-region` header, the **request won't be automatically re-routed** to another region. +If you explicitly specify the region via `x-region` header, requests **will NOT** be automatically re-routed to another region and you should consider temporarily changing regions during the outage. export const Page = ({ children }) => diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx index 8259d4e2d12..81f59125dd7 100644 --- a/apps/docs/pages/guides/functions/secrets.mdx +++ b/apps/docs/pages/guides/functions/secrets.mdx @@ -2,8 +2,9 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { id: 'functions-secrets', - title: 'Managing Secrets and Environment Variables', + title: 'Managing Environment Variables', description: 'Managing secrets and environment variables.', + subtitle: 'Managing secrets and environment variables.', } It's common that you will need to use sensitive information or environment-specific variables inside your Edge Functions. You can access these using Deno's built-in handler @@ -12,25 +13,21 @@ It's common that you will need to use sensitive information or environment-speci Deno.env.get(MY_SECRET_NAME) ``` -### Local Development +## Default secrets -When developing functions locally, you will be able to load environment variables in two ways: - -1. Through a default `.env` file placed at `supabase/functions/.env`, which will get loaded on `supabase start` -2. Through the `--env-file` option for `supabase functions serve`, for example: `supabase functions serve --env-file ./path/to/.env-file` - -To perform a one-time setup of your local development secrets, use the first option to create the `.env` file that will apply to all functions. - -### Default secrets - -By default, Edge Functions have access to these secrets: +Edge Functions have access to these secrets by default: - `SUPABASE_URL`: The API gateway for your Supabase project. - `SUPABASE_ANON_KEY`: The `anon` key for your Supabase API. This is safe to use in a browser when you have [Row Level Security](/docs/guides/auth/row-level-security) enabled. - `SUPABASE_SERVICE_ROLE_KEY`: The `service_role` key for your Supabase API. This is safe to use in Edge Functions, but it should NEVER be used in a browser. This key will bypass [Row Level Security](/docs/guides/auth/row-level-security). - `SUPABASE_DB_URL`: The URL for your [PostgreSQL database](/docs/guides/database). You can use this to connect directly to your database. -### Local secrets +## Local secrets + +You can load environment variables in two ways: + +1. Through an `.env` file placed at `supabase/functions/.env`, which is automatically loaded on `supabase start` +2. Through the `--env-file` option for `supabase functions serve`, for example: `supabase functions serve --env-file ./path/to/.env-file` Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`: @@ -60,7 +57,7 @@ supabase functions serve --env-file ./supabase/.env.local When the function starts you should see the name “Yoda” output to the terminal. -### Production secrets +## Production secrets Let's create a `.env` for production. In this case we'll just use the same as our local secrets: diff --git a/apps/docs/pages/guides/functions/troubleshooting.mdx b/apps/docs/pages/guides/functions/troubleshooting.mdx deleted file mode 100644 index 33e0978d261..00000000000 --- a/apps/docs/pages/guides/functions/troubleshooting.mdx +++ /dev/null @@ -1,64 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'functions-troubleshooting', - title: 'Troubleshooting Edge Functions', - description: 'Troubleshooting Supabase Edge Functions.', -} - -## Unable to call Edge Function - -If you're unable to call your Edge Function or are experiencing any CORS issues, even though you followed the [CORS guide](/docs/guides/functions/cors), please head over to [supabase.com/dashboard/project/\_/functions](https://supabase.com/dashboard/project/_/functions), select your function from the list & click Logs. Do you see any errors listed there? - -There are two debugging tools available: Invocations and Logs. Invocations shows the Request and Response for each execution, while Logs shows any platform events, including deployments and errors. - -## Unable to deploy Edge Function - -- Make sure you're on the latest version of the [Supabase CLI](/docs/guides/cli#updates). -- Run the deploy command with the `--debug` flag. -- Run the deploy command with the `--legacy-bundle` flag and see if that works. -- If the output from the commands above does not help you to resolve the issue, please open a support ticket via the Supabase Dashboard (by clicking the "Help" button at the top right) and include all output from the commands mentioned above. - -## Edge Function takes too long to respond - -- Head over to [supabase.com/dashboard/project/\_/functions](https://supabase.com/dashboard/project/_/functions), select your function from the list & click Logs. -- In the logs, look for the `booted` event and check if they have consistent boot times. - - If the boot times are similar, it's likely an issue with your function's code, like a large dependency etc. - - If only some of the `booted` events are slow, please find the affected `region` in the metadata and submit a support request via the "Help" button at the top. - -## Issues serving Edge Functions locally with the Supabase CLI - -- Make sure you're on the latest version of the [Supabase CLI](/docs/guides/cli#updates). -- Run the serve command with the `--debug` flag. -- Support engineers can then try to run the provided sample code locally and see if they can reproduce the issue. -- Search the [Edge Runtime](https://github.com/supabase/edge-runtime) and [CLI](https://github.com/supabase/cli) repos for the error message, to see if it has been reported before. -- If the output from the commands above does not help you to resolve the issue, please open a support ticket via the Supabase Dashboard (by clicking the "Help" button at the top right) and include all output and details about your commands. - -# Advanced Techniques - -## Checking Function Boot Time - -Check the logs for the function. In the logs, look for a "Booted" event and note the reported boot time. If available, click on the event to access more details, including the regions from where the function was served. Investigate if the boot time is excessively high (`higher than 1 second`) and note any patterns or regions where it occurs. - -## Finding Bundle Size - -To find the bundle size of a function, run the following command locally: - -```bash -deno info /path/to/function/index.ts -``` - -Look for the "size" field in the output which represents an approximated the bundle size of the function.You can find the accurate bundle size when you deploy your function via Supabase CLI. If the function is part of a larger application, consider examining the bundle size of the specific function independently. - -## Analyze Dependencies - -Review the dependencies listed in the output of the `deno info` command. Pay attention to any significantly large dependencies, as they can contribute to increased bundle size and potential boot time issues. -Examine if there are any unnecessary or redundant dependencies that can be removed. Check for outdated dependencies and recommend updating to the latest versions if applicable. When running deno info make sure to provide the correct path of the import map if you use one. - -```bash -deno info --import-map=/path/to/import_map.json /path/to/function/index.ts -``` - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/functions/typescript-support.mdx b/apps/docs/pages/guides/functions/typescript-support.mdx deleted file mode 100644 index 7816f28fac7..00000000000 --- a/apps/docs/pages/guides/functions/typescript-support.mdx +++ /dev/null @@ -1,54 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'functions-debugging', - title: 'TypeScript Support', - description: 'Using TypeScript in Deno Edge Functions.', -} - -One of the benefits of Deno is that it treats TypeScript as a first class -language, just like JavaScript or Web Assembly, when running code in Deno. What -that means is you can run or import TypeScript without installing anything more -than the Deno CLI. - -## How does it work? - -At a high level, Deno converts TypeScript (as well as TSX and JSX) into -JavaScript. It does this via a combination of the -[TypeScript compiler](https://github.com/microsoft/TypeScript), which we build -into Deno, and a Rust library called [swc](https://swc.rs/). When the code has -been type checked and transformed, it is stored in a cache, ready for the next -run without the need to convert it from its source to JavaScript again. - -## Strict by default - -Deno type checks TypeScript in _strict_ mode by default, and the TypeScript core -team recommends _strict_ mode as a sensible default. This mode generally enables -features of TypeScript that probably should have been there from the start, but -as TypeScript continued to evolve, would be breaking changes for existing code. - -## Mixing JavaScript and TypeScript - -By default, Deno does not type check JavaScript. This can be changed, and is -discussed further in [Configuring TypeScript in Deno](https://deno.com/manual/advanced/typescript/configuration). Deno -does support JavaScript importing TypeScript and TypeScript importing -JavaScript, in complex scenarios. - -An important note though is that when type checking TypeScript, by default Deno -will "read" all the JavaScript in order to be able to evaluate how it might have -an impact on the TypeScript types. The type checker will do the best it can to -figure out what the types are of the JavaScript you import into TypeScript, -including reading any JSDoc comments. Details of this are discussed in detail in -the [Types and type declarations](https://deno.com/manual/advanced/typescript/types) section. - -## Type resolution - -One of the core design principles of Deno is to avoid non-standard module -resolution, and this applies to type resolution as well. If you want to utilize -JavaScript that has type definitions (e.g. a `.d.ts` file), you have to -explicitly tell Deno about this. The details of how this is accomplished are -covered in the [Types and type declarations](https://deno.com/manual/advanced/typescript/types) section. - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/functions/unit-test.mdx b/apps/docs/pages/guides/functions/unit-test.mdx index ef23086e14b..0006dc6869e 100644 --- a/apps/docs/pages/guides/functions/unit-test.mdx +++ b/apps/docs/pages/guides/functions/unit-test.mdx @@ -4,15 +4,37 @@ export const meta = { id: 'unit-test', title: 'Testing your Edge Functions', description: 'Writing Unit Tests for Edge Functions using Deno Test', + subtitle: 'Writing Unit Tests for Edge Functions using Deno Test', } Testing is an essential step in the development process to ensure the correctness and performance of your Edge Functions. -When creating test files for Edge Functions, it is recommended to place them in the `supabase/tests` directory and name the file by the function name followed by `-test.ts`. For example, `hello-world-test.ts`. +## Testing in Deno -```typescript -// deno-test.ts +Deno has a built-in test runner that you can use for testing JavaScript or TypeScript code. You can read the [official documentation](https://docs.deno.com/runtime/manual/basics/testing/) for more information and details about the available testing functions. +## Folder structure + +We recommend creating your testing in a `supabase/tests` directory, using the same name as the Function followed by `-test.ts`: + +```bash +└── supabase + ├── functions + │ ├── function-one + │ │ └── index.ts + │ └── function-two + │ │ └── index.ts + │ └── tests + │ └── function-one-test.ts # Tests for function-one + │ └── function-two-test.ts # Tests for function-two + └── config.toml +``` + +## Example script + +The following script is a good example to get started with testing your Edge Functions: + +```typescript function-one-test.ts // Import required libraries and modules import { assert, @@ -93,7 +115,7 @@ Please make sure to replace the placeholders (`supabaseUrl`, `supabaseKey`, `my_ -### Running Edge Functions Locally +## Running Edge Functions Locally To locally test and debug Edge Functions, you can utilize the Supabase CLI. Let's explore how to run Edge Functions locally using the Supabase CLI: @@ -127,7 +149,7 @@ To locally test and debug Edge Functions, you can utilize the Supabase CLI. Let' 4. To run the tests, use the following command in your terminal: ```bash - deno test --allow-all deno-test.ts --env-file .env.local + deno test --allow-all supabase/functions/function-one-test.ts --env-file .env.local ``` ## Resources diff --git a/apps/www/lib/redirects.js b/apps/www/lib/redirects.js index 93edbf97994..9daf63e354f 100644 --- a/apps/www/lib/redirects.js +++ b/apps/www/lib/redirects.js @@ -2328,4 +2328,14 @@ module.exports = [ source: '/docs/guides/functions/global-deployments', destination: '/docs/guides/functions/regional-invocation', }, + { + permanent: true, + source: '/docs/guides/functions/typescript-support', + destination: '/docs/guides/functions', + }, + { + permanent: true, + source: '/docs/guides/functions/troubleshooting', + destination: '/docs/guides/functions/debugging', + }, ]