docs/guides/frameworks/supabase-edge-functions-database-webhooks.mdx
import Prerequisites from "/snippets/framework-prerequisites.mdx"; import SupabasePrerequisites from "/snippets/supabase-prerequisites.mdx"; import UsefulNextSteps from "/snippets/useful-next-steps.mdx"; import TriggerTaskNextjs from "/snippets/trigger-tasks-nextjs.mdx"; import NextjsTroubleshootingMissingApiKey from "/snippets/nextjs-missing-api-key.mdx"; import NextjsTroubleshootingButtonSyntax from "/snippets/nextjs-button-syntax.mdx"; import SupabaseDocsCards from "/snippets/supabase-docs-cards.mdx"; import SupabaseAuthInfo from "/snippets/supabase-auth-info.mdx";
Supabase and Trigger.dev can be used together to create powerful workflows triggered by real-time changes in your database tables:
A Supabase Database Webhook triggers an Edge Function when a row including a video URL is inserted into a table
The Edge Function triggers a Trigger.dev task, passing the video_url column data from the new table row as the payload
The Trigger.dev task then:
<Card title="View the project on GitHub" icon="GitHub" href="https://github.com/triggerdotdev/examples/tree/main/supabase-edge-functions"
Click here to view the full code for this project in our examples repository on GitHub. You can fork it and use it as a starting point for your own project. </Card>
The easiest way to get started is to use the CLI. It will add Trigger.dev to your existing project, create a /trigger folder and give you an example task.
Run this command in the root of your project to get started:
<CodeGroup>npx trigger.dev@latest init
pnpm dlx trigger.dev@latest init
yarn dlx trigger.dev@latest init
It will do a few things:
trigger.config.ts file in the root of your project./trigger directory./trigger directory with an example task, /trigger/example.[ts/js].Choose "None" when prompted to install an example task. We will create a new task for this guide.
</Step> </Steps>First, in the Supabase project dashboard, you'll need to create a new table to store the video URL and transcription.
To do this, click on 'Table Editor' <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" /> in the left-hand menu and create a new table. <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" />
Call your table video_transcriptions. <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" />
Add two new columns, one called video_url with the type text <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" />, and another called transcription, also with the type text <Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" />.
To allow you to use TypeScript to interact with your table, you need to generate the type definitions for your Supabase table using the Supabase CLI.
supabase gen types --lang=typescript --project-id <project-ref> --schema public > database.types.ts
<Note> Replace <project-ref> with your Supabase project reference ID. This can be found in your Supabase project settings under 'General'. </Note>
Create a new task file in your /trigger folder. Call it videoProcessAndUpdate.ts.
This task takes a video from a public video url, extracts the audio using FFmpeg and transcribes the audio using Deepgram. The transcription summary will then be updated back to the original row in the video_transcriptions table in Supabase.
You will need to install some additional dependencies for this task:
<CodeGroup>npm install @deepgram/sdk @supabase/supabase-js fluent-ffmpeg
pnpm install @deepgram/sdk @supabase/supabase-js fluent-ffmpeg
yarn install @deepgram/sdk @supabase/supabase-js fluent-ffmpeg
These dependencies will allow you to interact with the Deepgram and Supabase APIs and extract audio from a video using FFmpeg.
<Warning> When updating your tables from a Trigger.dev task which has been triggered by a database change, be extremely careful to not cause an infinite loop. Ensure you have the correct conditions in place to prevent this. </Warning>// Install any missing dependencies below
import { createClient as createDeepgramClient } from "@deepgram/sdk";
import { createClient as createSupabaseClient } from "@supabase/supabase-js";
import { logger, task } from "@trigger.dev/sdk";
import ffmpeg from "fluent-ffmpeg";
import fs from "fs";
import { Readable } from "node:stream";
import os from "os";
import path from "path";
import { Database } from "../../database.types";
// Create a single Supabase client for interacting with your database
// 'Database' supplies the type definitions to supabase-js
const supabase = createSupabaseClient<Database>(
// These details can be found in your Supabase project settings under `API`
process.env.SUPABASE_PROJECT_URL as string, // e.g. https://abc123.supabase.co - replace 'abc123' with your project ID
process.env.SUPABASE_SERVICE_ROLE_KEY as string // Your service role secret key
);
// Your DEEPGRAM_SECRET_KEY can be found in your Deepgram dashboard
const deepgram = createDeepgramClient(process.env.DEEPGRAM_SECRET_KEY);
export const videoProcessAndUpdate = task({
id: "video-process-and-update",
run: async (payload: { videoUrl: string; id: number }) => {
const { videoUrl, id } = payload;
logger.log(`Processing video at URL: ${videoUrl}`);
// Generate temporary file names
const tempDirectory = os.tmpdir();
const outputPath = path.join(tempDirectory, `audio_${Date.now()}.wav`);
const response = await fetch(videoUrl);
// Extract the audio using FFmpeg
await new Promise((resolve, reject) => {
if (!response.body) {
return reject(new Error("Failed to fetch video"));
}
ffmpeg(Readable.from(response.body))
.outputOptions([
"-vn", // Disable video output
"-acodec pcm_s16le", // Use PCM 16-bit little-endian encoding
"-ar 44100", // Set audio sample rate to 44.1 kHz
"-ac 2", // Set audio channels to stereo
])
.output(outputPath)
.on("end", resolve)
.on("error", reject)
.run();
});
logger.log(`Audio extracted from video`, { outputPath });
// Transcribe the audio using Deepgram
const { result, error } = await deepgram.listen.prerecorded.transcribeFile(
fs.readFileSync(outputPath),
{
model: "nova-2", // Use the Nova 2 model
smart_format: true, // Automatically format the transcription
diarize: true, // Enable speaker diarization
}
);
if (error) {
throw error;
}
const transcription = result.results.channels[0].alternatives[0].paragraphs?.transcript;
logger.log(`Transcription: ${transcription}`);
// Delete the temporary audio file
fs.unlinkSync(outputPath);
logger.log(`Temporary audio file deleted`, { outputPath });
const { error: updateError } = await supabase
.from("video_transcriptions")
// Update the transcription column
.update({ transcription: transcription })
// Find the row by its ID
.eq("id", id);
if (updateError) {
throw new Error(`Failed to update transcription: ${updateError.message}`);
}
return {
message: `Summary of the audio: ${transcription}`,
result,
};
},
});
Before you can deploy the task, you'll need to add the FFmpeg build extension to your trigger.config.ts file.
// Add this import
import { ffmpeg } from "@trigger.dev/build/extensions/core";
import { defineConfig } from "@trigger.dev/sdk";
export default defineConfig({
project: "<project ref>", // Replace with your project ref
// Your other config settings...
build: {
// Add the FFmpeg build extension
extensions: [ffmpeg()],
},
});
If you are modifying this example and using popular FFmpeg libraries like fluent-ffmpeg you'll also need to add them to external in your trigger.config.ts file.
You will need to add your DEEPGRAM_SECRET_KEY, SUPABASE_PROJECT_URL and SUPABASE_SERVICE_ROLE_KEY as environment variables in your Trigger.dev project. This can be done in the 'Environment Variables' page in your project dashboard.
Now you can now deploy your task using the following command:
<CodeGroup>npx trigger.dev@latest deploy
pnpm dlx trigger.dev@latest deploy
yarn dlx trigger.dev@latest deploy
Go to your Trigger.dev project dashboard and copy the prod secret key from the API keys page.
Then, in Supabase, select the project you want to use, navigate to 'Project settings' <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" />, click 'Edge Functions' <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" /> in the configurations menu, and then click the 'Add new secret' <Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" /> button.
Add TRIGGER_SECRET_KEY <Icon icon="circle-4" iconType="solid" size={20} color="A8FF53" /> with the pasted value of your Trigger.dev prod secret key.
Now create an Edge Function using the Supabase CLI. Call it video-processing-handler. This function will be triggered by the Database Webhook.
supabase functions new video-processing-handler
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { tasks } from "npm:@trigger.dev/sdk@latest";
// Import the videoProcessAndUpdate task from the trigger folder
import type { videoProcessAndUpdate } from "../../../src/trigger/videoProcessAndUpdate.ts";
// 👆 type only import
// Sets up a Deno server that listens for incoming JSON requests
Deno.serve(async (req) => {
const payload = await req.json();
// This payload will contain the video url and id from the new row in the table
const videoUrl = payload.record.video_url;
const id = payload.record.id;
// Trigger the videoProcessAndUpdate task with the videoUrl payload
await tasks.trigger<typeof videoProcessAndUpdate>("video-process-and-update", { videoUrl, id });
console.log(payload ?? "No name provided");
return new Response("ok");
});
Now deploy your new Edge Function with the following command:
supabase functions deploy video-processing-handler
Follow the CLI instructions, selecting the same project you added your prod secret key to, and once complete you should see your new Edge Function deployment in your Supabase Edge Functions dashboard.
There will be a link to the dashboard in your terminal output.
In your Supabase project dashboard, click 'Project settings' <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" />, then the 'API' tab <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" />, and copy the anon public API key from the table <Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" />.
Then, go to 'Database' <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" /> click on 'Webhooks' <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" />, and then click 'Create a new hook' <Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" />.
<Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" /> Call the hook edge-function-hook.
<Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" /> Select the new table you have created:
public video_transcriptions.
<Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" /> Choose the insert event.
<Icon icon="circle-4" iconType="solid" size={20} color="A8FF53" /> Under 'Webhook configuration', select 'Supabase Edge Functions'{" "}
<Icon icon="circle-5" iconType="solid" size={20} color="A8FF53" /> Under 'Edge Function', choose POST
and select the Edge Function you have created: video-processing-handler.{" "}
<Icon icon="circle-6" iconType="solid" size={20} color="A8FF53" /> Under 'HTTP Headers', add a new header with the key Authorization and the value Bearer <your-api-key> (replace <your-api-key> with the anon public API key you copied earlier).
<Icon icon="circle-7" iconType="solid" size={20} color="A8FF53" /> Click 'Create webhook'.{" "}
Your Database Webhook is now ready to use.
Your video-processing-handler Edge Function is now set up to trigger the videoProcessAndUpdate task every time a new row is inserted into your video_transcriptions table.
To do this, go back to your Supabase project dashboard, click on 'Table Editor' <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" /> in the left-hand menu, click on the video_transcriptions table <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" /> , and then click 'Insert', 'Insert Row' <Icon icon="circle-3" iconType="solid" size={20} color="A8FF53" />.
Add a new item under video_url, with a public video url. <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" />.
You can use the following public video URL for testing: https://content.trigger.dev/Supabase%20Edge%20Functions%20Quickstart.mp4.
Once the new table row has been inserted, check your cloud.trigger.dev project 'Runs' list <Icon icon="circle-1" iconType="solid" size={20} color="A8FF53" /> and you should see a processing videoProcessAndUpdate task <Icon icon="circle-2" iconType="solid" size={20} color="A8FF53" /> which has been triggered when you added a new row with the video url to your video_transcriptions table.
Once the run has completed successfully, go back to your Supabase video_transcriptions table, and you should see that in the row containing the original video URL, the transcription has now been added to the transcription column.
Congratulations! You have completed the full workflow from Supabase to Trigger.dev and back again.
<SupabaseDocsCards />