Supabase functions in dart - Complete Guide

25 April 2023

â€ĸ

7 min read

â€ĸ Mobile development

One of the latest feature introduced by Supabase is Edge Functions:

  • Server-side functions
  • Distributed globally at the edge, close to your users (low latency)
  • Used for listening to webhooks or integrating your Supabase project with third-parties services

Edge functions in Supabase are generally written in Typescript. Nonetheless, a new package called edge allows you to implement edge functions in dart đŸŽ¯

In this post, we are going to setup a new project to implement your first edge function on Supabase in Dart.

Note: Dart Edge is an experimental project. It's probably not ready for production yet ☝ī¸


Setting up edge

The first step is installing and activating the edge CLI by running the following command:

# install the edge CLI
dart pub global activate edge
# install the edge CLI
dart pub global activate edge

This will give access to the edge command in the terminal (from Dart Edge).

You can use the edge command to create a new project:

edge new supabase_functions new_project
edge new supabase_functions new_project

new_project is the name of the project (name of the folder created by running the command)

Developing a Supabase edge function in dart

Some extra dependencies are required to run functions on the edge:

pubspec.yaml
dependencies:
  # Installed when initializing the project
  edge: ^0.0.6
  supabase_functions: ^0.0.1
 
  # Extra dependencies
  supabase: ^1.6.3
  edge_http_client: ^0.0.1+1
pubspec.yaml
dependencies:
  # Installed when initializing the project
  edge: ^0.0.6
  supabase_functions: ^0.0.1
 
  # Extra dependencies
  supabase: ^1.6.3
  edge_http_client: ^0.0.1+1

You can add them directly inside pubspec.yaml or by running the following commands:

dart pub add supabase
dart pub add edge_http_client
dart pub add supabase
dart pub add edge_http_client

Initializing Supabase project

The first required step is initializing Supabase by running init:

supabase init
supabase init

Note: Make sure you have the Supabase CLI installed on your device, which gives you access to the supabase command

Running this command will create a new supabase folder in your project containing all the required supabase configurations.

Initialized supabase folder configuration

Subscribe to the newsletter

In my newsletter I share ideas, essays, articles, and lessons learned about my interests, during my own personal journey (👨‍đŸ’ģ, 🕹ī¸, 🎌, ✍ī¸, 🚀). I work both on web and mobile, using Typescript + React and Dart + Flutter.

Develop a Supabase function in dart

First make sure you have installed all the dependencies by running pub get:

dart pub get
dart pub get

A Supabase function in dart is implemented by using SupabaseFunctions:

main.dart
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  SupabaseFunctions(fetch: (request) async {
    /// The function body here đŸ’ģ
  });
}
main.dart
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  SupabaseFunctions(fetch: (request) async {
    /// The function body here đŸ’ģ
  });
}

This function gives access to a request object (Request) containing all the information about the incoming request (method, headers, body, and more).

The function then expects you to return a Response. You can use Response.json to return your response as JSON:

main.dart
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  SupabaseFunctions(fetch: (request) async {
    return Response.json({ "someParam": "someData" });
  });
}
main.dart
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  SupabaseFunctions(fetch: (request) async {
    return Response.json({ "someParam": "someData" });
  });
}

The Response class provides also other constructors:

  • Response.error
  • Response.redirect

Finally you can start the application using the edge build command:

edge build supabase_functions --dev
edge build supabase_functions --dev

This command will compile your Dart code to Javascript (Deno), used to run edge functions in Supabase. You can see the result inside the new supabase/functions/dart_edge folder:

Supabase dart function compiled to javascript

By adding the --dev flag the system will start listening for changes and recompile the code each time.

Make request to Supabase

You can access your Supabase instance by using SupabaseClient (from the supabase package we installed previously):

main.dart
import 'package:edge_http_client/edge_http_client.dart';
import 'package:supabase/supabase.dart';
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  final supabase = SupabaseClient(
    Deno.env.get('SUPABASE_URL')!,
 
    /// Use service role key to bypass RLS (Row Level Security)
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
    
    httpClient: EdgeHttpClient(),
  );
 
  SupabaseFunctions(fetch: (request) async {
    /// You can query `public.users` table for example
    final users = await supabase.from('users').select().limit(10);
 
    return Response.json({
      'users': users,
    });
  });
}
main.dart
import 'package:edge_http_client/edge_http_client.dart';
import 'package:supabase/supabase.dart';
import 'package:supabase_functions/supabase_functions.dart';
 
void main() {
  final supabase = SupabaseClient(
    Deno.env.get('SUPABASE_URL')!,
 
    /// Use service role key to bypass RLS (Row Level Security)
    Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
    
    httpClient: EdgeHttpClient(),
  );
 
  SupabaseFunctions(fetch: (request) async {
    /// You can query `public.users` table for example
    final users = await supabase.from('users').select().limit(10);
 
    return Response.json({
      'users': users,
    });
  });
}

Note: EdgeHttpClient comes from edge_http_client, and it is an http client implemented specifically for edge requests

Test your Supabase function locally

The last step is deploying the function on Supabase.

To set up a function locally using Docker, you can use the supabase functions serve command.

Make sure you have Docker installed on your local machine.

Start the Supabase stack by running supabase start:

supabase start
supabase start

The supabase start command uses Docker to start the Supabase services:

  • Postgres database
  • Realtime Server
  • Supabase API

When you run this command, Docker will pull the necessary images and start the containers. Once all of the Supabase services are running, you will see the output containing your local Supabase credentials.

Note: This command may take a while to run if this is the first time using the CLI, as Docker needs to download the necessary images.

You can use the supabase stop command at any time to stop all services.

You can then deploy and test you function locally by running the following command:

supabase functions serve dart_edge --no-verify-jwt
supabase functions serve dart_edge --no-verify-jwt

Note: By default Supabase Functions expects a valid auth header (anon key). The --no-verify-jwt enables you to call the function without a valid auth header

You can now access your Supabase function at http://localhost:54321/functions/v1/dart_edge.

Deploy Supabase Functions remotely

Once you are ready to deploy your function remotely on Supabase first make sure to run the build command:

edge build supabase_functions --dev
edge build supabase_functions --dev

Note: In the current version of dart_edge (v0.0.6) the edge build supabase_functions command does not work (issue on Github). Therefore, to compile your function you need to add the --dev flag.

You then need to stop the process that is listening for changes, and finally run the following to deploy on Supabase:

supabase functions deploy dart_edge
supabase functions deploy dart_edge

dart_edge is the name of your Supabase function

You will be prompted to insert your project reference id, that you can find in the URL of your Supabase dashboard https://app.supabase.com/project/<your-project-id>, or inside the Project Settings in your dashboard:

Supabase reference id in Supabase dashboard

You can link your local project to your Supabase instance to avoid having to provide the Supabase reference every time you deploy.

You can now access your function at https://<your-project-id>.functions.supabase.co/dart_edge.

Note: Supabase functions require you to always provide an Authorization header containing an authorization key.

Remember to send this header with your request, otherwise you will get a 401 error: "Missing authorization header"

Subscribe to the newsletter

In my newsletter I share ideas, essays, articles, and lessons learned about my interests, during my own personal journey (👨‍đŸ’ģ, 🕹ī¸, 🎌, ✍ī¸, 🚀). I work both on web and mobile, using Typescript + React and Dart + Flutter.

Packages used to write Supabase Edge functions

As we saw previously, some packages are required to implement and deploy Supabase edge functions in dart. We are now going to see a brief overview of them.

supabase

The supabase package is used to access your Supabase instance.

This package provides all you need to for:

edge_http_client

The edge_http_client package is an http client implemented specifically for edge requests. It is built on top of http and edge_runtime.

It exports a single EdgeHttpClient which extends Client from the http package.

You need to provide EdgeHttpClient as custom client when initializing SupabaseClient:

final supabase = SupabaseClient(
  /// `https://<your-project-id>.supabase.co` 👇
  Deno.env.get('SUPABASE_URL')!,
 
  /// Anon key 👇
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
 
  /// Custom http client
  httpClient: EdgeHttpClient(),
);
final supabase = SupabaseClient(
  /// `https://<your-project-id>.supabase.co` 👇
  Deno.env.get('SUPABASE_URL')!,
 
  /// Anon key 👇
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!,
 
  /// Custom http client
  httpClient: EdgeHttpClient(),
);

Deno.env provides access to your Supabase url and key directly as environmental variables once deployed on Supabase

yet_another_json_isolate

We did not install this package directly in this post, but this is another package developed specifically for SupabaseClient (used internally).

yet_another_json_isolate is JSON parsing library that uses isolates to parse JSON.

Isolates in Dart are used to implement concurrent programming: independent workers that are similar to threads but don't share memory, communicating only via messages.

Isolates allow multithreading in Dart: isolates do not share memory with other isolates, instead each new isolate will contain its own memory and event loops.

Two isolates can communicate with each other by passing messages back and forth.

This allow a more efficient JSON parsing implementation.


edge is still an experimental project. You can expect many new features and bug fixes in the next months. Bookmark this post to stay always up to date with the latest additions and upgrades ☑ī¸

You can also follow @SandroMaglione (me 👋) and subscribe to my newsletter here below to receive live updates on Dart, Flutter, Web & Mobile Development 👇

Thanks for reading.