diff --git a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts index 7abe1bea978..75d54fb2afb 100644 --- a/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts +++ b/apps/docs/components/Navigation/NavigationMenu/NavigationMenu.constants.ts @@ -421,14 +421,28 @@ export const functions = { label: 'Edge Functions', url: '/guides/functions', items: [ + { name: 'Overview', url: '/guides/functions/overview', items: [] }, { name: 'Auth', url: '/guides/functions/auth', items: [] }, + { name: 'CORS', url: '/guides/functions/cors', items: [] }, { name: 'CI/CD Workflow', url: '/guides/functions/cicd-workflow', items: [] }, - { name: 'Best Practices', url: '/guides/functions/best-practices', items: [] }, - { name: 'Quickstart', url: '/guides/functions/quickstart', items: [] }, + { name: 'Managing Secrets', url: '/guides/functions/secrets', items: [] }, + { name: 'Testing & Debugging', url: '/guides/functions/testing-debugging', items: [] }, + { name: 'Tips', url: '/guides/functions/tips', items: [] }, { name: 'Examples', url: '/guides/functions/examples', - items: [{ name: 'OG Image', url: '/guides/functions/examples/og-image', items: [] }], + items: [ + { + name: 'Cloudflare Turnstile', + url: '/guides/functions/examples/cloudflare-turnstile', + items: [], + }, + { name: 'GitHub Actions', url: '/guides/functions/examples/github-actions', items: [] }, + { name: 'OG Image', url: '/guides/functions/examples/og-image', items: [] }, + { name: 'Storage Caching', url: '/guides/functions/examples/storage-caching', items: [] }, + { name: 'Stripe Webhooks', url: '/guides/functions/examples/stripe-webhooks', items: [] }, + { name: 'Telegram Bot', url: '/guides/functions/examples/telegram-bot', items: [] }, + ], }, ], } diff --git a/apps/docs/pages/guides/functions/cicd-workflow.mdx b/apps/docs/pages/guides/functions/cicd-workflow.mdx index e902f140370..fd7826ddf0e 100644 --- a/apps/docs/pages/guides/functions/cicd-workflow.mdx +++ b/apps/docs/pages/guides/functions/cicd-workflow.mdx @@ -35,6 +35,15 @@ jobs: - run: supabase functions deploy your-function-name --project-ref $PROJECT_ID ``` +
+ +
+ 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/cors.mdx b/apps/docs/pages/guides/functions/cors.mdx new file mode 100644 index 00000000000..fca25fa4933 --- /dev/null +++ b/apps/docs/pages/guides/functions/cors.mdx @@ -0,0 +1,59 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'functions-cors', + title: 'CORS (Cross-Origin Resource Sharing)', + description: 'Add CORS headers to invoke functions from the browser.', +} + +To invoke the functions from the browser, you need to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests. + +See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts). + +### Recommended setup + +We recommend adding a `corst.ts` file within a `_shared` folder which makes it easy to reuse the CORS headers across functions: + +```ts cors.ts +export const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +} +``` + +You can then import and use the CORS headers within your functions: + +```ts index.ts +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import { corsHeaders } from '../_shared/cors.ts' + +console.log(`Function "browser-with-cors" up and running!`) + +serve(async (req) => { + // This is needed if you're planning to invoke your function from a browser. + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }) + } + + try { + const { name } = await req.json() + const data = { + message: `Hello ${name}!`, + } + + return new Response(JSON.stringify(data), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 200, + }) + } catch (error) { + return new Response(JSON.stringify({ error: error.message }), { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 400, + }) + } +}) +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx b/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx new file mode 100644 index 00000000000..560b3d9e454 --- /dev/null +++ b/apps/docs/pages/guides/functions/examples/cloudflare-turnstile.mdx @@ -0,0 +1,96 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'examples-cloudflare-turnstile', + title: 'Cloudflare Turnstile', + description: 'Protecting Forms with Cloudflare Turnstile.', +} + +
+ +
+ +[Clouflare Turnstile](https://www.cloudflare.com/products/turnstile/) is a friendly, free CAPTCHA replacement, and it works seamlessly with Supabase Edge Functions to protect your forms. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/cloudflare-turnstile). + +## Setup + +- Follow these steps to set up a new site: https://developers.cloudflare.com/turnstile/get-started/ +- Add the Cloudflare Turnstile widget to your site: https://developers.cloudflare.com/turnstile/get-started/client-side-rendering/ + +## Code + +Create a new function in your project: + +```bash +supabase functions new cloudflare-turnstile +``` + +And add the code to the `index.ts` file: + +```ts index.ts +import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' +import { corsHeaders } from '../_shared/cors.ts' + +console.log('Hello from Cloudflare Trunstile!') + +function ips(req: Request) { + return req.headers.get('x-forwarded-for')?.split(/\s*,\s*/) +} + +serve(async (req) => { + // This is needed if you're planning to invoke your function from a browser. + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }) + } + + const { token } = await req.json() + const clientIps = ips(req) || [''] + const ip = clientIps[0] + + // Validate the token by calling the + // "/siteverify" API endpoint. + let formData = new FormData() + formData.append('secret', Deno.env.get('CLOUDFLARE_SECRET_KEY') ?? '') + formData.append('response', token) + formData.append('remoteip', ip) + + const url = 'https://challenges.cloudflare.com/turnstile/v0/siteverify' + const result = await fetch(url, { + body: formData, + method: 'POST', + }) + + const outcome = await result.json() + console.log(outcome) + if (outcome.success) { + return new Response('success', { headers: corsHeaders }) + } + return new Response('failure', { headers: corsHeaders }) +}) +``` + +## Deploy the server-side validation Edge Functions + +- https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ + +```bash +supabase functions deploy cloudflare-turnstile +supabase secrets set CLOUDFLARE_TURNSTILE_SECRET_KEY=your_secret_key +``` + +## Invoke the function from your site + +```js +const { data, error } = await supabase.functions.invoke('cloudflare-turnstile', { + body: { token }, +}) +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/examples/github-actions.mdx b/apps/docs/pages/guides/functions/examples/github-actions.mdx new file mode 100644 index 00000000000..97456f33767 --- /dev/null +++ b/apps/docs/pages/guides/functions/examples/github-actions.mdx @@ -0,0 +1,49 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'examples-github-actions', + title: 'GitHub Actions', + description: 'Deploying Edge Functions with GitHub Actions.', +} + +
+ +
+ +Use the Supabase CLI together with GitHub Actions to automatically deploy our Supabase Edge Functions. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/github-action-deploy). + +```yaml deploy.yaml +name: Deploy Function + +on: + push: + branches: + - main + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + + env: + SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }} + PROJECT_ID: zdtdtxajzydjqzuktnqx + + steps: + - uses: actions/checkout@v3 + + - uses: supabase/setup-cli@v1 + with: + version: 1.0.0 + + - run: supabase functions deploy github-action-deploy --project-ref $PROJECT_ID +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/examples/og-image.mdx b/apps/docs/pages/guides/functions/examples/og-image.mdx index 2974d5ef853..e4be6c85796 100644 --- a/apps/docs/pages/guides/functions/examples/og-image.mdx +++ b/apps/docs/pages/guides/functions/examples/og-image.mdx @@ -17,6 +17,10 @@ export const meta = { Generate Open Graph images with Deno and Supabase Edge Functions. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/opengraph). +## Code + +Create a `handler.tsx` file to construct the OG image in React: + ```tsx handler.tsx import React from 'https://esm.sh/react@18.2.0' import { ImageResponse } from 'https://deno.land/x/og_edge@0.0.4/mod.ts' @@ -42,6 +46,8 @@ export default function handler(req: Request) { } ``` +Create an `index.ts` file to execute the handler on incoming requests: + ```ts index.ts import { serve } from 'https://deno.land/std@0.131.0/http/server.ts' import handler from './handler.tsx' diff --git a/apps/docs/pages/guides/functions/examples/storage-caching.mdx b/apps/docs/pages/guides/functions/examples/storage-caching.mdx new file mode 100644 index 00000000000..6e4b6ff604e --- /dev/null +++ b/apps/docs/pages/guides/functions/examples/storage-caching.mdx @@ -0,0 +1,22 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'examples-storage-caching', + title: 'Caching Images with Supabase Storage CDN', + description: 'Integrate Edge Functions with Supabase Storage to cache images on the Edge (CDN).', +} + +
+ +
+ +Integrate Edge Functions with Supabase Storage to cache images on the Edge (CDN). [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/og-image-with-storage-cdn). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx b/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx new file mode 100644 index 00000000000..5b75bb48878 --- /dev/null +++ b/apps/docs/pages/guides/functions/examples/stripe-webhooks.mdx @@ -0,0 +1,22 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'examples-stripe-webhooks', + title: 'Handling Stripe Webhooks', + description: 'Handling signed Stripe Webhooks with Edge Functions.', +} + +
+ +
+ +Handling signed Stripe Webhooks with Edge Functions. [View on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/stripe-webhooks/index.ts). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/examples/telegram-bot.mdx b/apps/docs/pages/guides/functions/examples/telegram-bot.mdx new file mode 100644 index 00000000000..bac6a45eba2 --- /dev/null +++ b/apps/docs/pages/guides/functions/examples/telegram-bot.mdx @@ -0,0 +1,22 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'examples-telegram-bot', + title: 'Telegram Bot', + description: 'Building a Telegram Bot with Edge Functions.', +} + +
+ +
+ +Handle Telegram Bot Webhooks with the [grammY framework](https://grammy.dev/). grammY is an open source Telegram Bot Framework which makes it easy to handle and respond to incoming messages. [View on GitHub](https://github.com/supabase/supabase/tree/master/examples/edge-functions/supabase/functions/telegram-bot). + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/overview.mdx b/apps/docs/pages/guides/functions/overview.mdx new file mode 100644 index 00000000000..49935c280bf --- /dev/null +++ b/apps/docs/pages/guides/functions/overview.mdx @@ -0,0 +1,84 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'functions-overview', + title: 'Edge Functions Overview', + description: 'Globally distributed TypeScript Functions.', + sidebar_label: 'Overview', +} + +Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes. + +
+ +
+ +## Prerequisites + +Follow the steps to prepare your Supabase project on your local machine. + +- 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/cli/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). +- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor. + +## Create a function + +Let's create a new Edge Function called `hello-world` inside your project: + +```bash +supabase functions new hello-world +``` + +This creates a function stub in your `supabase` folder at `./functions/hello-world/index.ts`. + +## Deploy to production + +```bash +supabase functions deploy hello-world +``` + +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. + + + +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. + + + +## Execute remote functions + +You can execute Edge Functions using curl. Copy the curl command from the Dashboard. It should look like this: + +```bash +curl --request POST 'https://.functions.supabase.co/hello-world' \ + --header 'Authorization: Bearer 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`. + +After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`. + +## Limitations + +- Deno Deploy limitations + - Deno does not support outgoing connections to ports `25`, `465`, and `587`. + - Cannot write to File System +- Edge Functions + - Local development - only one function at a time + - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`). + +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 deleted file mode 100644 index 55b9ed617c6..00000000000 --- a/apps/docs/pages/guides/functions/quickstart.mdx +++ /dev/null @@ -1,210 +0,0 @@ -import Layout from '~/layouts/DefaultGuideLayout' - -export const meta = { - id: 'functions-quickstart', - title: 'Edge Functions Quickstart', - description: 'Globally distributed TypeScript functions.', - sidebar_label: 'Quickstart', -} - -Learn how to build an Edge Function locally and deploy it to the Supabase Platform in less than 7 minutes. - -
- -
- -## Prerequisites - -Follow the steps to prepare your Supabase project on your local machine. - -- 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/cli/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). -- Optional: Setup your environment: Follow [this setup guide](https://deno.land/manual/getting_started/setup_your_environment) to integrate the Deno language server with your editor. - -## Create a function - -Let's create a new Edge Function called `hello-world` inside your project: - -```bash -supabase functions new hello-world -``` - -This creates a function stub in your `supabase` folder at `./functions/hello-world/index.ts`. - -## Deploy to production - -```bash -supabase functions deploy hello-world -``` - -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. - - - -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. - - - -## Execute remote functions - -You can execute Edge Functions using curl. Copy the curl command from the Dashboard. It should look like this: - -```bash -curl --request POST 'https://.functions.supabase.co/hello-world' \ - --header 'Authorization: Bearer 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`. - -After invoking your Edge Function you should see the response `{ "message":"Hello Functions!" }`. - -## Debug functions - -You can debug your deployed Edge Functions using the "Functions" section of the Dashboard. There are two types debugging tools available: - -- Invocations: shows the Request and Response for each execution. -- Logs: shows any platform events, including deployments and errors. - -![Function invocations.](/docs/img/guides/functions/function-logs.png) - -## Develop 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 hello-world # start the Function watcher -``` - -The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server. - -### Invoke functions locally - -While serving your local Function, you can execute it using curl: - -```bash -curl --request POST 'http://localhost:54321/functions/v1/hello-world' \ - --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ - --header 'Content-Type: application/json' \ - --data '{ "name":"Functions" }' -``` - -You should see the response `{ "message":"Hello Functions!" }`. - -
-Implementation details - -- 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`) - -
-
- -If you execute 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! - -## 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 - -```js -Deno.env.get(MY_SECRET_NAME) -``` - -### Default secrets - -By default, Edge Functions have access to these secrets: - -- `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 - -Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`: - -```jsx -echo "MY_NAME=Yoda" >> ./supabase/.env.local -``` - -This creates a new file `./supabase/.env.local` for storing your local development secrets. - - - -Never check your .env files into Git! - - - -Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line: - -```jsx -console.log(Deno.env.get('MY_NAME')) -``` - -Now we can invoke our function locally, by serving it with our new `.env.local` file: - -```bash -supabase functions serve hello-world --env-file ./supabase/.env.local -``` - -When the function starts you should see the name “Yoda” output to the terminal. - -### Production secrets - -Let's create a `.env` for production. In this case we'll just use the same as our local secrets: - -```bash -cp ./supabase/.env.local ./supabase/.env -``` - -This creates a new file `./supabase/.env` for storing your production secrets. - - - -Never check your `.env` files into Git! - - - -Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set): - -```bash -supabase secrets set --env-file ./supabase/.env - -# You can also set secrets individually using: -supabase secrets set MY_NAME=Chewbacca -``` - -You don't need to re-deploy after setting your secrets. - -To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list): - -```bash -supabase secrets list -``` - -## Limitations - -- Deno Deploy limitations - - Deno does not support outgoing connections to ports `25`, `465`, and `587`. - - Cannot write to File System -- Edge Functions - - Local development - only one function at a time - - Serving of HTML content is not supported (`GET` requests that return `text/html` will be rewritten to `text/plain`). - -export const Page = ({ children }) => - -export default Page diff --git a/apps/docs/pages/guides/functions/secrets.mdx b/apps/docs/pages/guides/functions/secrets.mdx new file mode 100644 index 00000000000..dd51094d374 --- /dev/null +++ b/apps/docs/pages/guides/functions/secrets.mdx @@ -0,0 +1,89 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'functions-secrets', + title: 'Secrets and Environment Variables', + description: '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 + +```js +Deno.env.get(MY_SECRET_NAME) +``` + +### Default secrets + +By default, Edge Functions have access to these secrets: + +- `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 + +Let's create a local file for storing our secrets, and inside it we can store a secret `MY_NAME`: + +```jsx +echo "MY_NAME=Yoda" >> ./supabase/.env.local +``` + +This creates a new file `./supabase/.env.local` for storing your local development secrets. + + + +Never check your .env files into Git! + + + +Now let's access this environment variable `MY_NAME` inside our Function. Anywhere in your function, add this line: + +```jsx +console.log(Deno.env.get('MY_NAME')) +``` + +Now we can invoke our function locally, by serving it with our new `.env.local` file: + +```bash +supabase functions serve hello-world --env-file ./supabase/.env.local +``` + +When the function starts you should see the name “Yoda” output to the terminal. + +### Production secrets + +Let's create a `.env` for production. In this case we'll just use the same as our local secrets: + +```bash +cp ./supabase/.env.local ./supabase/.env +``` + +This creates a new file `./supabase/.env` for storing your production secrets. + + + +Never check your `.env` files into Git! + + + +Let's push all the secrets from the `.env` file to our remote project using [`supabase secrets set`](/docs/reference/cli/usage#supabase-secrets-set): + +```bash +supabase secrets set --env-file ./supabase/.env + +# You can also set secrets individually using: +supabase secrets set MY_NAME=Chewbacca +``` + +You don't need to re-deploy after setting your secrets. + +To see all the secrets which you have set remotely, use [`supabase secrets list`](/docs/reference/cli/usage#supabase-secrets-list): + +```bash +supabase secrets list +``` + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/testing-debugging.mdx b/apps/docs/pages/guides/functions/testing-debugging.mdx new file mode 100644 index 00000000000..b0b4cc468dd --- /dev/null +++ b/apps/docs/pages/guides/functions/testing-debugging.mdx @@ -0,0 +1,55 @@ +import Layout from '~/layouts/DefaultGuideLayout' + +export const meta = { + id: 'functions-testing-debugging', + title: 'Testing and Debugging Functions', + description: 'Test and debug functions locally and in production.', +} + +You can debug your deployed Edge Functions using the "Functions" section of the Dashboard. There are two types debugging tools available: + +- Invocations: shows the Request and Response for each execution. +- Logs: shows any platform events, including deployments and errors. + +![Function invocations.](/docs/img/guides/functions/function-logs.png) + +## Develop 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 hello-world # start the Function watcher +``` + +The `functions serve` command has hot-reloading capabilities. It will watch for any changes to your files and restart the Deno server. + +### Invoke functions locally + +While serving your local Function, you can execute it using curl: + +```bash +curl --request POST 'http://localhost:54321/functions/v1/hello-world' \ + --header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24ifQ.625_WdcF3KHqz5amU0x2X5WWHP-OEs_4qj0ssLNHzTs' \ + --header 'Content-Type: application/json' \ + --data '{ "name":"Functions" }' +``` + +You should see the response `{ "message":"Hello Functions!" }`. + +
+Implementation details + +- 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`) + +
+
+ +If you execute 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! + +export const Page = ({ children }) => + +export default Page diff --git a/apps/docs/pages/guides/functions/best-practices.mdx b/apps/docs/pages/guides/functions/tips.mdx similarity index 71% rename from apps/docs/pages/guides/functions/best-practices.mdx rename to apps/docs/pages/guides/functions/tips.mdx index b670cd73a05..8d11169805a 100644 --- a/apps/docs/pages/guides/functions/best-practices.mdx +++ b/apps/docs/pages/guides/functions/tips.mdx @@ -1,9 +1,9 @@ import Layout from '~/layouts/DefaultGuideLayout' export const meta = { - id: 'functions-best-practices', - title: 'Edge Functions Best Practices', - description: 'Globally distributed TypeScript functions.', + id: 'functions-tips', + title: 'Edge Functions Tips', + description: 'Misc Tips for Supabase Edge Functions.', } ## Database Functions vs Edge Functions @@ -35,26 +35,6 @@ We recommend developing “fat functions”. This means that you should develop We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase). -## CORS (Cross-Origin Resource Sharing) - -We recommend adding a check to handle [CORS Preflight](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) requests in your edge function to be able to invoke the function from browsers. - -See the [example on GitHub](https://github.com/supabase/supabase/blob/master/examples/edge-functions/supabase/functions/browser-with-cors/index.ts). - -```ts -export const corsHeaders = { - 'Access-Control-Allow-Origin': '*', - 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey', -} - -serve(async (req) => { - if (req.method === 'OPTIONS') { - return new Response('ok', { headers: corsHeaders }) - } - ... -}) -``` - ## Using HTTP Methods 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.