Supabase Backend

Add authentication, a database, and Edge Functions to your extension. Everything runs locally before you deploy.

Requires ChromeShip Pro

Upgrade to Pro to unlock the Supabase backend.

Enable Supabase

In the Integrations tab, enable Supabase. ChromeShip configures the client, auth helpers, and database access. The Supabase URL and anon key are managed automatically — never hardcode them.

Integrations tab with Supabase enabled

Local stack

On the Overview tab, click Start under Supabase Functions. A project-isolated Docker stack launches with PostgreSQL, Auth, PostgREST, Storage, and API Gateway. Each project gets its own stack — starting another project's backend stops the active one automatically.

The first time you start the backend, ChromeShip handles several things automatically:

  • Supabase CLI — installed via npm into a managed directory if not already present. This runs once per machine.
  • Docker images — pulled from the registry (PostgreSQL, Auth, PostgREST, Storage, Kong). This takes 2-5 minutes the first time depending on your connection.
  • Migrations — database migrations are applied to initialize the schema.
  • Functions serve — the Deno edge runtime starts and waits for your functions to be ready (up to 30-second timeout).

Subsequent starts skip the downloads and pulls — the backend starts in 20-50 seconds. Stopping the functions runtime also stops the containers to free resources.

ChromeShip automatically patches functions:dev in your package.json to remove supabase migration up if it was added by the CLI — so migrations don't replay on every start.

Overview tab showing Supabase Functions runtime started

Auth

Supabase auth works across all extension surfaces. The session is persisted using an MV3-compatible storage adapter — no cookies, no localStorage. Use the auth object for everything.

import { auth } from "@/integrations/supabase";

// Email + password
await auth.signUp(email, password);
await auth.signIn(email, password);

// OAuth (Google, GitHub, etc.)
await auth.signInWithOAuth("google");
// Opens a browser OAuth flow, exchanges the code, and returns the session

await auth.signOut();

// Check session on mount
const user = await auth.getUser();           // { id, email } | null
const session = await auth.getSession();     // full token + user + expiry
const signedIn = await auth.isAuthenticated();

// React to sign-in / sign-out across all surfaces
auth.onChange((session) => {
  if (session.isAuthenticated) showApp(); else showLogin();
});

OAuth in extensions uses chrome.identity.launchWebAuthFlow — the redirect URL is configured automatically by ChromeShip. No manual setup needed.

Database

db talks directly to Supabase PostgREST. It is protected by Row-Level Security policies — configure them in the Supabase Studio. Every query is scoped to the policies you define.

import { db } from "@/integrations/supabase";

const { data } = await db.from("profiles").select("*");
await db.from("profiles").insert({ name: "Jane", plan: "pro" });
await db.from("profiles").update({ plan: "free" }).eq("id", userId);
await db.from("profiles").delete().eq("id", userId);

Storage

Store user files in Supabase buckets. Public URLs for avatars and shared files. Signed URLs for private documents with time-limited access.

import { storage } from "@/integrations/supabase";

await storage.upload("avatars", "user-1.png", file, { upsert: true });
const blob = await storage.download("exports", "report.pdf");
const url = storage.getPublicUrl("avatars", "user-1.png");
const signed = await storage.createSignedUrl("private", "doc.pdf", 120); // valid 120s

Edge Functions

Call your Supabase Edge Functions from extension code. Secrets stay in the function — the extension never sees API keys, Stripe secrets, or provider credentials.

functions.invoke — the standard call. Serializes the body to JSON, sets content-type, attaches the auth token, parses the JSON response, and wraps errors in ChromeShipError. Use this for 99% of calls.

import { functions } from "@/integrations/supabase";
const result = await functions.invoke("hello-world", { name: "World" });

functions.raw — full control. Returns the raw Response object without parsing or error normalization. Use for downloads (.blob()), non-JSON responses, or custom headers.

const response = await functions.raw("download-report", {
  method: "POST",
  body: { id: "123" },
});
const blob = await response.blob();

Migrations

In the Functions tab, create forward-only SQL migrations. ChromeShip timestamps every new file automatically. Migrations are forward-only — do not edit an already-applied migration. Add a new corrective migration instead. Deploy from the Functions tab with a single click.

Deploy to production

Deploy migrations and functions to your remote Supabase project from the Functions tab. Production environment variables are used automatically. Test everything locally before deploying.