Auto-process Supabase uploads with Ittybit
Upload a file to Supabase Storage and have it automatically processed by Ittybit. A database webhook triggers an Edge Function on insert, that function sends the file to Ittybit, and a webhook callback writes the results back to your Supabase database. Zero manual steps.
Architecture
- User uploads to Supabase Storage
- Database webhook on
storage.objectsinsert fires an Edge Function - Edge Function POSTs to Ittybit
/taskswith the fileโs public URL - Ittybit processes the file and sends a webhook on completion
- A second Edge Function receives the webhook and upserts results into a
mediatable
Create the media table
create table public.media (
id uuid primary key default gen_random_uuid(),
storage_path text not null,
source_url text not null,
task_id text,
status text default 'pending',
output_url text,
metadata jsonb,
created_at timestamptz default now(),
updated_at timestamptz default now()
);
Edge Function: trigger processing
This function runs when a new row appears in storage.objects. It builds the public URL for the uploaded file and creates an Ittybit task.
// supabase/functions/process-upload/index.ts
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
serve(async (req) => {
const payload = await req.json();
const record = payload.record;
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);
// Build the public URL for the uploaded file
const { data: urlData } = supabase.storage
.from(record.bucket_id)
.getPublicUrl(record.name);
const sourceUrl = urlData.publicUrl;
// Insert a pending row in the media table
const { data: media } = await supabase
.from("media")
.insert({
storage_path: `${record.bucket_id}/${record.name}`,
source_url: sourceUrl,
status: "pending",
})
.select()
.single();
// Create an Ittybit task
const task = await fetch("https://api.ittybit.com/jobs", {
method: "POST",
headers: {
Authorization: `Bearer ${Deno.env.get("ITTYBIT_API_KEY")}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
input: sourceUrl,
kind: "video",
options: {
width: 1280,
format: "mp4",
quality: "high",
},
metadata: {
media_id: media.id,
},
}),
});
const result = await task.json();
// Store the task ID
await supabase
.from("media")
.update({ task_id: result.id, status: "processing" })
.eq("id", media.id);
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
});
# Test the flow manually by creating a task with a Supabase Storage URL
curl -X POST https://api.ittybit.com/jobs \
-H "Authorization: Bearer $ITTYBIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"input": "https://your-project.supabase.co/storage/v1/object/public/uploads/video.mp4",
"kind": "video",
"options": {
"width": 1280,
"format": "mp4",
"quality": "high"
}
}' Wire up the database webhook
In the Supabase Dashboard, go to Database > Webhooks and create a new webhook:
- Table:
storage.objects - Events:
INSERT - Type: Supabase Edge Function
- Function:
process-upload
Every file upload now triggers processing automatically.
Edge Function: receive Ittybit webhook
This function receives the callback from Ittybit when processing completes and updates your media table.
// supabase/functions/ittybit-webhook/index.ts
import { serve } from "https://deno.land/std@0.177.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
serve(async (req) => {
const payload = await req.json();
const supabase = createClient(
Deno.env.get("SUPABASE_URL")!,
Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!,
);
const mediaId = payload.metadata?.media_id;
if (!mediaId) {
return new Response("Missing media_id", { status: 400 });
}
await supabase
.from("media")
.update({
status: payload.status === "completed" ? "completed" : "failed",
output_url: payload.output?.url ?? null,
metadata: payload,
updated_at: new Date().toISOString(),
})
.eq("id", mediaId);
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
});
# Register your webhook endpoint in the Ittybit dashboard
# or via the API:
curl -X POST https://api.ittybit.com/webhooks \
-H "Authorization: Bearer $ITTYBIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-project.supabase.co/functions/v1/ittybit-webhook",
"events": ["job.succeeded", "job.failed"]
}' Deploy the Edge Functions
supabase functions deploy process-upload
supabase functions deploy ittybit-webhook
Set your secrets:
supabase secrets set ITTYBIT_API_KEY=your_ittybit_api_key
SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY are available automatically in Edge Functions.
Test the pipeline
Upload a file to your Supabase Storage bucket and query the media table to watch it progress:
select id, storage_path, status, output_url, created_at
from media
order by created_at desc
limit 5;
The row should move from pending to processing to completed, with output_url populated once Ittybit finishes.
See also
- Build a user upload pipeline โ multi-task processing for uploads
- Process files from S3 โ use S3-compatible storage as input