Auto-generate titles, descriptions & tags for Supabase uploads

View Markdown

Introduction

In this guide, you will:

  • Upload a file to Supabase Storage.
  • Get a title, description, and tags from the ittybit tasks api.
  • Set up a webhook receiver function.
  • Insert the results into your Supabase database.

Step 1: Create a Supabase storage bucket

You can use an existing storage bucket or create a new one.

If you haven't already created a bucket, you can do so with the following SQL script with the Supabase SQL editor:

insert into storage.buckets (id, name)
values ('ittybit-storage', 'ittybit-storage');

Step 2: Upload a file to Supabase storage bucket

We will start by uploading a media file to the bucket.

Here's a sample (using Supabase Edge Functions) which uploads a file to a bucket called ittybit-storage.

// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";

const supabase = createClient(
  Deno.env.get("SUPABASE_URL"),
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")
);

Deno.serve(async (req) => {
  const formData = await req.formData();
  const file = formData.get("file") as File;

  const bucket = supabase.storage.from("ittybit-storage");
  const path = `<your-folder>/${file.name}`;

  const { data: uploadData, error: uploadError } = await bucket.upload(
    path,
    file,
    {
      contentType: file.type,
    }
  );
  if (uploadError) throw uploadError;
  console.log("Upload data:", uploadData);
});

Step 3: Get a signed URL

A signed URL gives ittybit temporary access to a file in your supabase bucket.

Deno.serve(async (req) => {
  // ... the rest of your code ...
  console.log("Upload data:", uploadData);

  const { data: signedUrlData, error: signedUrlError } =
    await bucket.createSignedUrl(path, 60);

  if (signedUrlError) throw signedUrlError;

  const signedUrl = signedUrlData.signedUrl;

  console.log("Signed URL:", signedUrl);
});

Step 4: Send a POST request to ittybit tasks API

Prerequisite: This section uses the ittybit TypeScript SDK. Make sure it's installed in your project.

npm install @ittybit/sdk

Next, use the ittybit SDK to create an intelligence task with the signed URL from Supabase Storage.

If you don’t have a webhook endpoint ready, you can use a webhook site placeholder (such as webhook.site) for the webhook_url.

import { IttybitClient } from "@ittybit/sdk";

const ittybit = new IttybitClient({ 
  apiKey: process.env.ITTYBIT_API_KEY!
});

Deno.serve(async (req) => {
  // ... the rest of your code ...
  console.log("Signed URL:", signedUrl);

  const webhookUrl = "https://webhook.site/<your-uuid>";
  const task = await ittybit.tasks.create({
    url: signedUrl,
    kind: "description",
    metadata: {
      object_id: objectId,
    },
    webhook_url: webhookUrl,
  });

  console.log("Task created:", task.id);

  return new Response(JSON.stringify({ data: task }), {
    headers: { "Content-Type": "application/json" },
  });

Step 5: Create a table to store the intelligence results

Run the below SQL script in your Supabase SQL editor.

CREATE TABLE IF NOT EXISTS public.ittybit_intelligence (
    id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
    title varchar,
    description varchar,
    tags jsonb,
    object_id uuid NOT NULL,
    CONSTRAINT ittybit_intelligence_object_fk
        FOREIGN KEY (object_id)
        REFERENCES storage.objects (id)
        ON DELETE CASCADE
);

This will create a table called ittybit_intelligence in your Supabase project.


Step 6: Set up a webhook receiver function

To capture task results from ittybit and insert them into your Supabase database, you need a webhook endpoint.

This endpoint listens for POST requests from ittybit once a task completes.

Here's a sample Supabase Edge Function:

This function validates the incoming request, then writes the results into ittybit_intelligence within your Supabase Project.

// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";

const supabaseUrl = Deno.env.get("SUPABASE_URL");
const supabaseServiceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");

if (!supabaseUrl || !supabaseServiceRoleKey) {
  throw new Error("SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set");
}

const supabase = createClient(supabaseUrl, supabaseServiceRoleKey);
const table = "ittybit_intelligence"

Deno.serve(async (req) => {
  const { title, description, tags, metadata } = await req.json();
  const object_id = metadata?.object_id;
  
  if (!object_id) {
    console.error("metadata.object_id is required");
    return new Response("Missing object_id", {
      status: 400
    });
  }
  
  const { error } = await supabase.from(table).insert([
    {
      object_id,
      title,
      description,
      tags
    }
  ]);
  
  if (error) {
    console.error(error);
    return new Response(`Insert failed: ${error.message}`, {
      status: 500
    });
  }
  
  console.log(`Successfully inserted ${object_id} into ${table}`)
  return new Response("Inserted into DB", {
    status: 200
  });
});

Step 7: Update the task's webhook_url

You can now set the webhook_url property to your Supabase Edge function's public URL.

PLACEHOLDER IMAGE FOR SUPABASE EDGE FUNCTIONS PUBLIC URL

// ... the rest of your code ...
const webhookUrl = "https://your-supabase-domain.supabase.co/functions/v1/ittybit-webhook";
// ... the rest of your code ...

You can now test end to end by uploading a file to Supabase Storage and checking the webhook endpoint for the results.


Full example

// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { createClient } from "jsr:@supabase/supabase-js@2";
import { IttybitClient } from "npm:@ittybit/sdk";

const supabase = createClient(
  Deno.env.get("SUPABASE_URL"),
  Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")
);

const ittybit = new IttybitClient({
  apiKey: Deno.env.get("ITTYBIT_API_KEY"),
});

Deno.serve(async (req) => {
  const formData = await req.formData();
  const file = formData.get("file") as File;

  const bucket = supabase.storage.from("ittybit-storage");
  const path = `<your-folder>/${file.name}`;

  const { data: uploadData, error: uploadError } = await bucket.upload(
    path,
    file,
    {
      contentType: file.type,
    }
  );
  if (uploadError) throw uploadError;

  const { data: signedUrlData, error: signedUrlError } =
    await bucket.createSignedUrl(path, 60);
  if (signedUrlError) throw signedUrlError;

  const objectId = uploadData.id;
  const signedUrl = signedUrlData.signedUrl;

  const webhookUrl = "https://webhook.site/<your-uuid>";
  const task = await ittybit.tasks.create({
    url: signedUrl,
    kind: "description",
    metadata: {
      object_id: objectId,
    },
    webhook_url: webhookUrl,
  });

  return new Response(JSON.stringify({ data: task }), {
    headers: { "Content-Type": "application/json" },
  });
});

Conclusion

When ittybit finishes processing the file, it will send the title, description, and tags to your webhook endpoint, and they will be inserted automatically into your Supabase database.

You now have a working pipeline where:

  • Files are uploaded to Supabase Storage.
  • Tasks are created in ittybit using signed URLs.
  • Once processing is complete, ittybit delivers the results to your webhook endpoint.
  • The webhook inserts the metadata directly into your Supabase database.

This gives you a fully automated flow without the need for manual polling.