Integrate FastPix with Supabase
The @fastpix/supabase package provides a CLI that creates a fastpix schema in your database, deploys the edge functions that process webhooks, and keeps your data current automatically.
What you’ll build
After setup, your Supabase database has a fastpix schema that contains the following tables:
mediastores your on-demand video assets and their metadata.live_streamsstores live stream configurations, status, and secrets.uploadsstores direct-upload sessions.webhook_eventsstores the raw webhook event log for debugging and auditing.sync_statestores backfill and reconcile bookkeeping.
The CLI also creates four edge functions (fastpix-webhook, fastpix-worker, fastpix-reconcile, and fastpix-backfill), a pgmq job queue, and two Supabase Cron jobs, a queue drain and a nightly reconcile, that drive the sync.
Prerequisites
Before you begin, make sure you have the following:
- A FastPix account with an API token ID and token secret. To generate them, see Activate your account.
- Access to the FastPix dashboard, so that you can create a webhook and copy its signing secret.
- A Supabase project.
- The Supabase CLI. Every command on this page runs it through
npx, so a global install is optional. - Node.js version 20 or later.
- Docker. See Start Docker.
- A tunneling tool such as ngrok, to expose your local webhook endpoint to FastPix.
Setup overview
Work through these in order. Each one depends on the one before it.
Start Docker
Local Supabase runs as Docker containers, so Docker must be running before anything else.
-
Install Docker Desktop (macOS and Windows) or Docker Engine (Linux).
-
Start it. On macOS and Windows, open the app and wait for the whale icon to stop animating.
-
Confirm it’s running:
If docker info prints server details, you’re ready. If it reports Cannot connect to the Docker daemon, Docker is installed but not started.
NOTE: Local Supabase starts about a dozen containers. Give Docker at least 4 GB of RAM.
Start Supabase
Initialize Supabase in your project and start it:
start prints your local credentials. To see them again at any time:
Find your token ID and secret in the FastPix dashboard. You create the webhook secret in step 4, when you configure the webhook.
Initialize the FastPix integration
With Supabase running, initialize the integration:
This copies three migrations into supabase/migrations for the job queue and cron jobs, creates the four edge functions in supabase/functions, and sets verify_jwt in config.toml — false for the webhook, because FastPix signs its requests instead of sending a Supabase JWT.
It then prompts you for four values:
NOTE: If you skip the database URL, no tables are created. The rest of the setup will look fine, but nothing will ever sync. Create them later with:
Now restart Supabase so it picks up the config.toml changes and loads the new functions:
NOTE: Don’t skip the restart. Without it, the webhook endpoint returns
401and FastPix won’t accept your URL.
Add the Vault secrets
The cron jobs call your edge functions over HTTP, and they read the function URL and service-role key from Supabase Vault so that neither value is committed in a migration.
Do this now, before you set up the webhook. Without these secrets, events arrive and sit in the queue forever — nothing drains, and no error is reported.
Open the SQL Editor at http://127.0.0.1:54323 and run:
Replace <SERVICE_ROLE_KEY> with the value from npx supabase status -o env.
Use host.docker.internal rather than localhost — the URL has to be reachable from inside the Postgres container, where localhost means the container itself.
Expose your local server
FastPix delivers webhooks over the public internet, so your local Supabase needs a public address.
-
In one terminal, expose port 54321:
Leave it running. Copy the forwarding address it prints, then add
/functions/v1/fastpix-webhookto it. That’s your webhook URL: -
In a second terminal, serve the edge functions:
Leave this running too.
-
Confirm the endpoint answers, using your own URL:
You should get back
ok. Don’t move on until you do — FastPix checks this URL next, and won’t save it otherwise.- A
404meansfunctions serveisn’t running, or your ngrok URL has changed. - A
401means you skipped the restart after initializing FastPix.
- A
Create the webhook
-
In the FastPix dashboard, go to Org Settings > Webhooks > Create new webhook, and enter the URL from the previous section. For more information, see Set up webhooks.
-
Copy the signing secret that FastPix shows you.
-
Open
supabase/functions/.envand paste it in:WARNING: Copy the secret exactly as the dashboard shows it. It’s base64, and the engine decodes it before verifying signatures — a re-typed or truncated value makes every check fail silently, with no rows and no error.
-
Restart the functions. Go to the terminal running
functions serve, pressCtrl+C, and start it again:This isn’t optional.
functions servereads.envonce at startup, so the secret you just pasted has no effect until you restart. You only need to restart this one process, not the whole Supabase stack.
NOTE: On ngrok’s free tier the URL changes every time you restart the tunnel. If that happens, update the URL in the FastPix dashboard. The signing secret stays the same.
-
In the FastPix dashboard, go to Org Settings > Webhooks > Create new webhook and enter that URL. For more information, see Set up webhooks.
-
Copy the signing secret from FastPix into
FASTPIX_WEBHOOK_SECRETinsupabase/functions/.env.
Verify that it works
Upload a media asset from the FastPix dashboard, then open the media table in the fastpix schema at http://127.0.0.1:54323. A row appears within a few seconds, and its mediaId matches the ID in FastPix.
If the row doesn’t appear, query the event log to see where processing stopped:
- No rows at all — FastPix isn’t reaching your webhook, or the signature is failing. Recheck your ngrok URL and the signing secret.
- Rows stuck at
received— nothing is draining the queue. Recheck the Vault secrets. - Rows at
failed— readlastError. It’s usually a wrong token ID or secret.
NOTE: Column names match the FastPix API, so they’re camelCase and need double quotes in SQL. Use
select "mediaId" from fastpix.media, notselect mediaId.
Environment variables
init writes these to supabase/functions/.env:
You don’t need to add SUPABASE_DB_URL — Supabase gives that to the edge functions on its own. It’s separate from the database URL init asked for, which the CLI used to create the tables.
Whenever you change this file, restart npx supabase functions serve.
Backfill existing data
If your FastPix account already contains media or live streams, backfill them:
The command prompts for your database URL and FastPix credentials, or reads them from the environment. To sync a single object type, pass it as an argument:
Backfill checkpoints as it goes, so if it stops you can run it again and it resumes.
To heal webhooks you missed, reconcile resources active in the last few hours:
Backfill programmatically
Deploy to production
The production sequence mirrors the local one: migrations, then Vault secrets, then functions, then the webhook.
-
Link your project:
-
Run the migrations.
db pushapplies the queue and cron migrations, andmigratecreates thefastpixtables. Find your connection string in the dashboard under Connect.NOTE: If your project already has migrations,
db pushfails with “Found local migration files to be inserted before the last migration on remote database.” The FastPix migrations are numbered0001–0003, which sorts ahead of Supabase’s timestamped filenames. Re-run withnpx supabase db push --include-all. -
Add the Vault secrets, the same as locally but with your production URL:
If a secret already exists, update it instead:
-
Set your API credentials as edge function secrets:
-
Push the config. This sends the
verify_jwtsettings — without it, the webhook returns401. -
Deploy the functions:
-
Create the production webhook. After you deploy, the
fastpix-webhookURL appears in the dashboard under Edge Functions. Register it in FastPix, then set the signing secret:Secrets take effect immediately. You don’t need to redeploy.
-
If your account has existing media, run
npx @fastpix/supabase backfillagainst your production database.
Then secure the tables as described below.
NOTE: The edge functions open a direct, non-pooled Postgres connection, and the drain cron fires every 10 seconds. On a small instance you may approach your connection limit — if you see connection errors, point the functions at Supavisor or slow the drain in
0003_fastpix_setup_cron_job.sql.
Secure the tables
WARNING: Until you complete this step, the
fastpixtables have no row-level security.live_streamsholdsstreamKeyandsrtSecret, which let anyone stream into your account, andwebhook_eventsstores raw payloads that can contain those same secrets. Never expose either table to your client.
In the SQL Editor, grant access to the service role and enable row-level security on every table:
Enabling row-level security with no policies denies everyone except the service role, which is the safe default. To show data in your app, create a view that exposes only the safe columns.
Query your data
For server-side queries, use the service-role key, which bypasses row-level security:
WARNING: The service-role key bypasses row-level security and belongs only in server-side code. To read FastPix data from the client, add your own policies or expose a view with only the safe columns.
Use the sync engine with any Postgres
If you don’t use Supabase, use @fastpix/fp-sync-engine directly. It’s a framework-free TypeScript library that works with any Postgres database and runs on both Node.js and Deno.
Create the schema once, then process webhooks in your own server. This example uses Express:
For the full API reference, see the @fastpix/fp-sync-engine README.
Troubleshoot
FastPix won’t save the webhook URL. It checks the URL before saving. Make sure ngrok and functions serve are both running, then curl the URL — you should get ok. If you get 401, you skipped the restart after init.
No rows in fastpix.webhook_events. FastPix isn’t reaching your webhook, or the signature is failing. Check that your ngrok URL is current, that FASTPIX_WEBHOOK_SECRET matches exactly, and that you restarted functions serve after editing .env.
Rows stuck at received. Nothing is draining the queue. Confirm both Vault secrets exist and hold the correct values.
fastpix schema or tables don’t exist. init skipped database setup. Run SUPABASE_DB_URL='postgresql://...' npx @fastpix/supabase migrate.
db push fails with “Found local migration files to be inserted before the last migration.” Re-run with npx supabase db push --include-all.
The webhook returns 401 in production. Either you didn’t run npx supabase config push, or the signing secret is wrong — check with npx supabase secrets list.
A SQL query returns a column-not-found error. Column names are camelCase and need double quotes: select "mediaId" from fastpix.media.
Cannot connect to the Docker daemon. Docker isn’t running. Start Docker Desktop, or on Linux run sudo systemctl start docker.