Developing Edge Functions locally
Get started with Edge Functions on your local machine.
Let's create a basic Edge Function on your local machine and then invoke it using the Supabase CLI.
Initialize a project#
Create a new Supabase project in a folder on your local machine:
_10supabase init
CLI not installed?
Check out the CLI Docs to learn how to install the Supabase CLI on your local machine.
Create an Edge Function#
Let's create a new Edge Function called hello-world
inside your project:
_10supabase functions new hello-world
This creates a function stub in your supabase
folder:
_10└── supabase_10 ├── functions_10 │ └── hello-world_10 │ │ └── index.ts ## Your function code_10 └── config.toml
Running Edge Functions locally#
You can run your Edge Function locally using supabase functions serve
:
_10supabase start # start the supabase stack_10supabase 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 or one of the client libraries:
Where is my SUPABASE_ANON_KEY?
Run supabase status
to see your local credentials.
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.
Next steps#
Check out the Deploy to Production 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.
_10supabase functions serve hello-world --no-verify-jwt
Be careful when using this flag, as it will allow anyone to invoke your Edge Function without a valid JWT. The Supabase client libraries automatically handle authorization.
Using HTTP Methods#
Edge Functions support GET
, POST
, PUT
, PATCH
, DELETE
, and OPTIONS
. A Function can be designed to perform different actions based on a request's HTTP method. See the example on building a RESTful service to learn how to handle different HTTP methods in your Function.
HTML not supported
HTML content is not supported. GET
requests that return text/html
will be rewritten to text/plain
.
Naming Edge Functions#
We recommend using hyphens to name functions because hyphens are the most URL-friendly of all the naming conventions (snake_case, camelCase, PascalCase).
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 including the name of the function followed by a -test
suffix.
We recommend this folder structure:
_16└── supabase_16 ├── functions_16 │ ├── import_map.json # A top-level import map to use across functions._16 │ ├── _shared_16 │ │ ├── supabaseAdmin.ts # Supabase client with SERVICE_ROLE key._16 │ │ └── supabaseClient.ts # Supabase client with ANON key._16 │ │ └── cors.ts # Reusable CORS headers._16 │ ├── function-one # Use hyphens to name functions._16 │ │ └── index.ts_16 │ └── function-two_16 │ │ └── index.ts_16 │ └── tests_16 │ └── function-one-test.ts_16 │ └── function-two-test.ts_16 ├── migrations_16 └── config.toml
Using config.toml#
Individual function configuration like JWT verification and import map location can be set via the config.toml
file.
Error Handling#
The supabase-js
library provides several error types that you can use to handle errors that might occur when invoking Edge Functions:
_15import { FunctionsHttpError, FunctionsRelayError, FunctionsFetchError } from '@supabase/supabase-js'_15_15const { data, error } = await supabase.functions.invoke('hello', {_15 headers: { 'my-custom-header': 'my-custom-header-value' },_15 body: { foo: 'bar' },_15})_15_15if (error instanceof FunctionsHttpError) {_15 const errorMessage = await error.context.json()_15 console.log('Function returned an error', errorMessage)_15} else if (error instanceof FunctionsRelayError) {_15 console.log('Relay error:', error.message)_15} else if (error instanceof FunctionsFetchError) {_15 console.log('Fetch error:', error.message)_15}
Database Functions vs Edge Functions#
For data-intensive operations we recommend using Database Functions, which are executed within your database and can be called remotely using the REST and GraphQL API.
For use-cases which require low-latency we recommend Edge Functions, which are globally-distributed and can be written in TypeScript.