Nextended.Aspire.Hosting.Supabase

Complete Supabase stack integration for .NET Aspire — PostgreSQL, GoTrue auth, PostgREST, Storage, Kong gateway, Studio dashboard, and Edge Functions, all wired up with sensible defaults for local development and Azure Container Apps deployment.

Overview

This package wraps the official Supabase open-source containers into an Aspire-friendly fluent API. With one AddSupabase("supabase") call you get a fully functional Supabase stack identical to what runs in production at supabase.com — but local, reproducible, and integrated into your Aspire AppHost.

Installation

dotnet add package Nextended.Aspire.Hosting.Supabase

Quick Start

var builder = DistributedApplication.CreateBuilder(args);

var supabase = builder.AddSupabase("supabase");

builder.Build().Run();

This starts a full stack with:

Service Default port
PostgreSQL database 54322
GoTrue (authentication) (internal, exposed via Kong)
PostgREST API (internal, exposed via Kong)
Storage API (internal, exposed via Kong)
Kong API Gateway 8000
Studio Dashboard 54323
Postgres Meta 8080
Edge Runtime 9000

Configuration

Every sub-resource is configurable via its own fluent method:

var supabase = builder.AddSupabase("supabase")
    .ConfigureDatabase(db => db
        .WithPassword("secure-password")
        .WithPort(54322))

    .ConfigureAuth(auth => auth
        .WithAutoConfirm(true)
        .WithJwtExpiration(3600)
        .WithSiteUrl("http://localhost:3000"))

    .ConfigureRest(rest => rest
        .WithSchemas("public", "storage", "graphql_public")
        .WithAnonRole("anon"))

    .ConfigureStorage(storage => storage
        .WithFileSizeLimit(52428800))   // 50 MB

    .ConfigureKong(kong => kong.WithPort(8000))
    .ConfigureMeta(meta => meta.WithPort(8080))
    .ConfigureStudio(studio => studio
        .WithPort(54323)
        .WithOrganizationName("My Org")
        .WithProjectName("My Project"))
    .ConfigureEdgeRuntime(edge => edge.WithPort(9000));

Direct container access

Each Configure* method has an overload that exposes the underlying container builder:

.ConfigureDatabase(
    db => db.WithPassword("password"),
    container => container
        .WithEnvironment("CUSTOM_VAR", "value")
        .WithVolume("my-volume", "/data"))

Syncing from a remote Supabase project

Pull schema, data, storage, and more from an existing cloud project:

const string projectRef = "your-project-ref";
const string serviceKey = "eyJhbGciOiJIUzI1NiIs...";   // service_role key

var supabase = builder.AddSupabase("supabase")
    .WithProjectSync(projectRef, serviceKey);

Fine-grained control via SyncOptions:

.WithProjectSync(
    projectRef,
    serviceKey,
    SyncOptions.Schema | SyncOptions.Data | SyncOptions.StorageBuckets);
Option What gets synced
Schema Table structures (columns, types, constraints)
Data Table data
Policies Row-Level Security policies (requires DB password)
Functions Stored procedures and functions (requires DB password)
Triggers Database triggers (requires DB password)
Types Custom types and enums (requires DB password)
Views Database views (requires DB password)
Indexes Table indexes (requires DB password)
StorageBuckets Bucket definitions
StorageFiles Bucket files (downloads from remote)
EdgeFunctions Edge Functions (requires Management API token)
AllSchema Everything schema-related
AllStorage StorageBuckets + StorageFiles
All The whole project

Full sync with all options and tokens:

.WithProjectSync(
    projectRef,
    serviceKey,
    SyncOptions.All,
    dbPassword,
    managementApiToken);

Where the keys come from

Key Location in Supabase Dashboard
Project Ref URL: https://supabase.com/dashboard/project/{project-ref}
Service Role Key Project Settings → API → service_role (secret)
Database Password Project Settings → Database → Database password
Management API Token Account (top right) → Access Tokens

Local migrations

Apply SQL migrations from a directory using the YYYYMMDDHHMMSS_description.sql naming convention:

var migrationsPath = Path.Combine(builder.AppHostDirectory, "..", "supabase", "migrations");

var supabase = builder.AddSupabase("supabase")
    .WithMigrations(migrationsPath);

Edge Functions

var edgeFunctionsPath = Path.Combine(builder.AppHostDirectory, "..", "supabase", "functions");

var supabase = builder.AddSupabase("supabase")
    .WithEdgeFunctions(edgeFunctionsPath);

Directory layout:

supabase/
  functions/
    hello-world/
      index.ts
    another-function/
      index.ts

Each function is invokable through Kong:

curl -X POST http://localhost:8000/functions/v1/hello-world \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ANON_KEY" \
  -d '{"name": "World"}'

Pre-registered users

For development and integration tests:

var supabase = builder.AddSupabase("supabase")
    .WithRegisteredUser("admin@example.com", "password123", "Admin User")
    .WithRegisteredUser("test@example.com",  "test1234",    "Test User");

Users are created with confirmed email status, get a profile in public.profiles if that table exists, and get an admin role in public.user_roles if that table exists.

Dashboard commands

Add a “Clear All Data” button to the Aspire dashboard:

var supabase = builder.AddSupabase("supabase")
    .WithClearCommand();

Truncates every table in the public schema on click — useful during integration test runs.

Accessing resources

Sub-resource builders

var supabase = builder.AddSupabase("supabase");

var kong     = supabase.GetKong();
var studio   = supabase.GetStudio();
var database = supabase.GetDatabase();
var auth     = supabase.GetAuth();
var rest     = supabase.GetRest();
var storage  = supabase.GetStorage();
var meta     = supabase.GetMeta();
var edge     = supabase.GetEdgeRuntime();

Keys and endpoints

var anonKey        = supabase.Resource.AnonKey;
var serviceRoleKey = supabase.Resource.ServiceRoleKey;
var kongEndpoint   = supabase.Resource.Kong!.GetEndpoint("http");

Frontend integration

Wire your frontend app to the local stack:

var supabase = builder.AddSupabase("supabase");

builder.AddNpmApp("frontend", "../frontend")
    .WithEnvironment("VITE_SUPABASE_URL",       supabase.Resource.Kong!.GetEndpoint("http"))
    .WithEnvironment("VITE_SUPABASE_ANON_KEY",  supabase.Resource.AnonKey);

Deployment

The stack is designed to deploy to Azure Container Apps via azd:

azd init
azd up

All containers and their configuration are translated 1:1 from the Aspire model into Bicep/ACA resources.

Complete example

var builder = DistributedApplication.CreateBuilder(args);

var migrationsPath    = Path.Combine(builder.AppHostDirectory, "..", "supabase", "migrations");
var edgeFunctionsPath = Path.Combine(builder.AppHostDirectory, "..", "supabase", "functions");

var supabase = builder.AddSupabase("supabase")
    .ConfigureDatabase(db => db.WithPassword("secure-pw"))
    .ConfigureAuth(auth => auth.WithAutoConfirm(true))
    .ConfigureStudio(s => s.WithProjectName("My Project"))
    .WithMigrations(migrationsPath)
    .WithEdgeFunctions(edgeFunctionsPath)
    .WithRegisteredUser("dev@example.com", "dev1234", "Developer")
    .WithClearCommand();

builder.AddNpmApp("frontend", "../frontend")
    .WithEnvironment("VITE_SUPABASE_URL",      supabase.Resource.Kong!.GetEndpoint("http"))
    .WithEnvironment("VITE_SUPABASE_ANON_KEY", supabase.Resource.AnonKey);

builder.Build().Run();

Supported frameworks

  • .NET 8.0
  • .NET 9.0
  • .NET 10.0
  • Nextended.Aspire — General-purpose Aspire extensions (Docker checks, conditional waits, environment helpers)