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

Use the ittybit Tasks API to create a summary task with the signed URL from Supabase Storage.

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

  const response = await fetch('https://api.ittybit.com/tasks', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get("ITTYBIT_API_KEY")}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      url: signedUrl,
      kind: "summary",
      metadata: {
        object_id: objectId,
      },
    }),
  });

  const task = await response.json();

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

  return new Response(JSON.stringify(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 succeeds.

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: Register your webhook endpoint

Register your Supabase Edge Function as a webhook endpoint using the ittybit API so it receives task completion notifications:

curl -X POST https://api.ittybit.com/webhooks/endpoints \
  -H "Authorization: Bearer YOUR_ITTYBIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-supabase-domain.supabase.co/functions/v1/ittybit-webhook"
  }'

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";

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;

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

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

  const response = await fetch('https://api.ittybit.com/tasks', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${Deno.env.get("ITTYBIT_API_KEY")}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      url: signedUrl,
      kind: "summary",
      metadata: {
        object_id: objectId,
      },
    }),
  });

  const task = await response.json();

  return new Response(JSON.stringify(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.