Hugging Face image captioning edge function complete code
import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
import { createClient } from 'npm:@supabase/supabase-js@2'
import { Database } from './types.ts'
console.log('Hello from `huggingface-image-captioning` function!')
const hf = new HfInference(Deno.env.get('HUGGINGFACE_ACCESS_TOKEN'))
type SoRecord = Database['storage']['Tables']['objects']['Row']
interface WebhookPayload {
type: 'INSERT' | 'UPDATE' | 'DELETE'
table: string
record: SoRecord
schema: 'public'
old_record: null | SoRecord
}
Deno.serve(async (req) => {
const payload: WebhookPayload = await req.json()
const soRecord = payload.record
const SUPABASE_SECRET_KEYS = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)
const supabaseAdminClient = createClient<Database>(
// Supabase API URL - env var exported by default when deployed.
Deno.env.get('SUPABASE_URL') ?? '',
// Supabase API SECRET KEY - env var exported by default when deployed.
SUPABASE_SECRET_KEYS['default'] ?? ''
)
// Construct image url from storage
const { data, error } = await supabaseAdminClient.storage
.from(soRecord.bucket_id!)
.createSignedUrl(soRecord.path_tokens!.join('/'), 60)
if (error) throw error
const { signedUrl } = data
// Run image captioning with Huggingface
const imgDesc = await hf.imageToText({
data: await (await fetch(signedUrl)).blob(),
model: 'nlpconnect/vit-gpt2-image-captioning',
})
// Store image caption in Database table
await supabaseAdminClient
.from('image_caption')
.insert({ id: soRecord.id!, caption: imgDesc.generated_text })
.throwOnError()
return new Response('ok')
})
Database webhook trigger for image captioning
A database webhook can be created in the Supabase Dashboard to trigger an edge function anytime a record is added to the storage.objects table. This enables automatic image captioning when files are uploaded to a storage bucket.
Hugging Face Inference API access via JavaScript SDK
Huggingface.js provides a convenient way to make calls to 100,000+ machine learning models. The HfInference class is imported from 'https://esm.sh/@huggingface/inference@2.3.2' and initialized with a Hugging Face access token from environment variables. The imageToText() method can be used to run image captioning with a specified model like 'nlpconnect/vit-gpt2-image-captioning'.
Hugging Face image captioning edge function setup steps
To set up image captioning with Hugging Face and Supabase: (1) Create a new Supabase project. (2) Create a storage bucket called 'images'. (3) Generate TypeScript types from the remote database. (4) Create a database table called 'image_caption' with an 'id' column of type uuid that references storage.objects.id and a 'caption' column of type text. (5) Regenerate TypeScript types to include the new image_caption table. (6) Deploy the function to Supabase using 'supabase functions deploy huggingface-image-captioning'. (7) Create a database webhook in the Supabase Dashboard to trigger the huggingface-image-captioning function anytime a record is added to the storage.objects table.
Generate TypeScript types for storage and public schemas
To generate a types.ts file for the storage and public schemas, run the command: supabase gen types typescript --project-id=your-project-ref --schema=storage,public > supabase/functions/huggingface-image-captioning/types.ts
Serve edge functions locally
Run `supabase functions serve --env-file ./supabase/.env.local --no-verify-jwt` to serve edge functions locally. The function will be available at `http://localhost:54321/functions/v1/openai`.
Test edge function with curl
Make a POST request to test: `curl -i --location --request POST http://localhost:54321/functions/v1/openai --header 'Content-Type: application/json' --data '{"query":"What is Supabase?}'`
Deploy edge function to cloud
Deploy with `supabase functions deploy --no-verify-jwt openai` and then set secrets with `supabase secrets set --env-file ./supabase/.env.local`.
Stream OpenAI responses word-by-word
Set `stream: true` in the openai.chat.completions.create call to stream GPT's response word-by-word back to the client instead of waiting for the entire response.
Create edge function with supabase CLI
Run `supabase functions new openai` to scaffold a new edge function. This creates a new edge function at `./supabase/functions/openai/index.ts`.
Edge function to call OpenAI completions API
Import OpenAI from `https://deno.land/x/openai@v4.24.0/mod.ts`. Create a Deno.serve handler that reads a JSON POST request with a `query` field, passes it to OpenAI's chat.completions.create with model `gpt-3.5-turbo`, and returns the text response. The example uses `stream: false` to wait for the complete response before returning.
OpenAI API key configuration for edge functions
Store the OpenAI API key in a `.env.local` file in the `./supabase` folder with the format `OPENAI_API_KEY=your-key-here`. Access the key in the edge function using `Deno.env.get('OPENAI_API_KEY')`.
Curl example for testing text-to-image Edge Function
Test a text-to-image Edge Function locally with:
curl --output result.jpg --location --request POST 'http://localhost:54321/functions/v1/text-to-image' \
--header 'Content-Type: application/json' \
--data '{"prompt":"Llama wearing sunglasses"}'
This saves the generated image to result.jpg locally.
Testing Edge Functions locally with Hugging Face
To serve an Edge Function locally, run 'supabase functions serve --env-file .env.local --no-verify-jwt'. The --env-file parameter loads environment variables from the .env.local file. The --no-verify-jwt flag disables JWT verification for testing purposes. In production, JWT tokens must be passed as Bearer tokens in the Authorization header.
Text-to-image Edge Function example with Hugging Face
Example Edge Function that performs text-to-image generation using Hugging Face Inference API:
import { HfInference } from 'https://esm.sh/@huggingface/inference@2.3.2'
const hf = new HfInference(Deno.env.get('HUGGING_FACE_ACCESS_TOKEN'))
Deno.serve(async (req) => {
const { prompt } = await req.json()
const image = await hf.textToImage(
{
inputs: prompt,
model: 'stabilityai/stable-diffusion-2',
},
{
use_cache: false,
}
)
return new Response(image)
})
This function accepts a JSON POST request with a 'prompt' parameter, calls the Hugging Face textToImage() method with the stabilityai/stable-diffusion-2 model, and returns the generated image as a Blob wrapped in a Response.
Edge Function setup for Hugging Face integration
To use Hugging Face with Edge Functions, first run 'supabase init' in your local project if not already initialized. Then create a new Edge Function using 'supabase functions new <function-name>'. Store the Hugging Face access token in a .env.local file with the key HUGGING_FACE_ACCESS_TOKEN. The token is accessible inside Edge Functions via Deno.env.get() and can be safely used server-side.
Testing hybrid_search Edge Function with cURL
To test a hybrid_search Edge Function, make a POST request: curl -i --location --request POST 'http://127.0.0.1:54321/functions/v1/hybrid-search' --header 'Authorization: Bearer <anonymous key>' --header 'Content-Type: application/json' --data '{"query":"Italian recipes with tomato sauce"}'
hybrid_search Edge Function example with OpenAI
Example Edge Function calling hybrid_search from JavaScript:
import { createClient } from 'npm:@supabase/supabase-js@2'
import OpenAI from 'npm:openai'
const supabaseUrl = Deno.env.get('SUPABASE_URL')!
const supabaseSecretKey = Deno.env.get('SUPABASE_SECRET_KEY')!
const openaiApiKey = Deno.env.get('OPENAI_API_KEY')!
Deno.serve(async (req) => {
const { query } = await req.json()
const openai = new OpenAI({ apiKey: openaiApiKey })
const embeddingResponse = await openai.embeddings.create({
model: 'text-embedding-3-large',
input: query,
dimensions: 512,
})
const [{ embedding }] = embeddingResponse.data
const supabase = createClient(supabaseUrl, supabaseSecretKey)
const { data: documents } = await supabase.rpc('hybrid_search', {
query_text: query,
query_embedding: embedding,
match_count: 10,
})
return new Response(JSON.stringify(documents), {
headers: { 'Content-Type': 'application/json' },
})
})
This example generates embeddings using OpenAI's text-embedding-3-large model with 512 dimensions, then calls the hybrid_search Postgres function via RPC with the query text, embedding, and match count.
Supabase Edge Functions semantic search model support
Supabase Edge Functions has built-in support for the gte-small embedding model. Other models can be accessed through third-party APIs like OpenAI, or run locally through libraries like Transformers.js for JavaScript implementations.
Example: Edge Function to notify Slack of branch action runs
This Edge Function listens for run completed events and sends notifications to a Slack channel:
```typescript
import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
console.log('Branching notification booted!')
const slack = Deno.env.get('SLACK_WEBHOOK_URL') ?? ''
Deno.serve(async (request) => {
const body = await request.json()
const blocks = [
{
type: 'header',
text: {
type: 'plain_text',
text: `Action run ${body.data.action_run.failure ? 'failed' : 'completed'}`,
emoji: true,
},
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Branch ref:*\n${body.data.project_ref}`,
},
{
type: 'mrkdwn',
text: `*Run ID:*\n${body.data.action_run.id}`,
},
],
},
{
type: 'section',
fields: [
{
type: 'mrkdwn',
text: `*Started at:*\n${body.data.action_run.created_at}`,
},
{
type: 'mrkdwn',
text: `*Completed at:*\n${body.timestamp}`,
},
],
},
{
type: 'section',
text: {
type: 'mrkdwn',
text: `<${body.data.details_url}|View logs>`,
},
},
]
const resp = await fetch(slack, {
method: 'POST',
body: JSON.stringify({
blocks,
}),
})
const message = await resp.text()
return new Response(
JSON.stringify({
message,
}),
{
status: 200,
}
)
})
```
Edge Functions JWT verification limitation
Edge Functions only support JWT verification via the `anon` and `service_role` JWT-based API keys. When using publishable and secret keys with Edge Functions, you must use the `--no-verify-jwt` option. The Supabase platform does not verify the `apikey` header when using Edge Functions in this way. Implement your own `apikey`-header authorization logic inside the Edge Function code itself.
Edge Functions use Deno runtime
Supabase Edge Functions run on Deno, a modern runtime for JavaScript and TypeScript. Deno source code is at github.com/denoland/deno, written in TypeScript/Rust, and licensed under MIT.
Regional edge function invocations
Execute an Edge Function in a region close to your database. This feature is generally available and fully available on self-hosted deployments.
Deno edge functions
Globally distributed TypeScript functions to execute custom business logic. This feature is generally available and fully available on self-hosted deployments.
NPM compatibility for edge functions
Edge functions natively support NPM modules and Node built-in APIs.
Edge Functions new environment variables
Supabase adds two new environment variables to Edge Functions: SUPABASE_PUBLISHABLE_KEYS and SUPABASE_SECRET_KEYS, alongside legacy SUPABASE_ANON_KEY and SUPABASE_SERVICE_ROLE_KEY. The new variables hold JSON objects keyed by name instead of plain strings.
Reading named secret keys from Edge Functions environment
To read a named secret key from Edge Functions, parse the SUPABASE_SECRET_KEYS JSON object and access the key by its name. For example: const secretKey = JSON.parse(Deno.env.get('SUPABASE_SECRET_KEYS')!)['default']. Multiple named keys are stored in the same object, each under its own name.
Edge Functions must use apikey header only
Send publishable and secret keys on the apikey header only in Edge Functions. If the key is also passed on the Authorization Bearer header, the platform tries to parse it as a JWT and rejects the request with 'Invalid JWT'. Set verify_jwt = false in the function configuration.
@supabase/server SDK for Edge Functions
The @supabase/server SDK removes boilerplate from Edge Functions by handling key reading from environment, parsing Authorization headers, and initializing user-scoped and admin clients. It is the recommended approach for new functions. Use auth: 'user' for functions called by clients and auth: 'secret' for functions called by backends.
@supabase/server withSupabase auth modes
The withSupabase wrapper accepts auth modes: 'user' validates the user's session JWT and provides ctx.supabase scoped to Row Level Security; 'secret' validates the secret key and provides ctx.supabaseAdmin bypassing Row Level Security. Use auth: 'secret:billing' or auth: 'publishable:web' to validate specific named keys.
@supabase/server withSupabase example with user auth
import { withSupabase } from 'npm:@supabase/server'
Deno.serve(
withSupabase({ auth: 'user' }, async (_req, ctx) => {
// ctx.supabase is scoped to the authenticated user
return Response.json({ email: ctx.userClaims?.email })
})
)
@supabase/server withSupabase example with secret auth
import { withSupabase } from 'npm:@supabase/server'
Deno.serve(
withSupabase({ auth: 'secret' }, async (_req, ctx) => {
// ctx.supabaseAdmin is authenticated with a valid secret key
return Response.json({ ok: true })
})
)
@supabase/server fetch handler export style
withSupabase returns a standard request handler and can be exported as a fetch handler: export default { fetch: withSupabase({ auth: 'user' }, async (_req, ctx) => { return Response.json({ email: ctx.userClaims?.email }) }) }. The fetch style is portable across Edge Functions, Cloudflare Workers, and Bun.
Test Edge Functions using Deno native tools
Edge Functions are powered by Deno, which provides native testing tools. The Supabase CLI extends this functionality with additional capabilities for testing Edge Functions.
Edge Functions errors troubleshooting table
Symptom: Edge Function 401/404/500/503/504/546; CPU/memory/wall-clock limit hit; won't deploy; boot error; WebSocket drop; esm.sh import fails. Layer: Edge Functions → function_edge_logs, function_logs. Troubleshooting guides: 401; 500; 503 boot; 504; 546 resource limit; Shutdown reasons; Deploy fails; esm.sh import.
Gzip-compressed Edge Function log receiver example
Example Edge Function to receive gzip-compressed logs using Node-compatible built-in APIs:
```ts
import { gunzipSync } from 'node:zlib'
Deno.serve(async (req) => {
try {
const contentEncoding = req.headers.get('content-encoding')
if (contentEncoding !== 'gzip') {
return new Response('Request body is not gzip compressed', { status: 400 })
}
const compressedBody = await req.arrayBuffer()
const decompressedBody = gunzipSync(new Uint8Array(compressedBody))
const data = JSON.parse(new TextDecoder().decode(decompressedBody))
console.log(`Received: ${data.length} logs.`)
return new Response('ok', { headers: { 'Content-Type': 'text/plain' } })
} catch (error) {
console.error('Error:', error)
return new Response('Error processing request', { status: 500 })
}
})
```
Uncompressed Edge Function log receiver example
Example Edge Function to receive uncompressed logs. Create with `supabase functions new log-receiver`, update the function body, and deploy with `supabase functions deploy log-receiver --project-ref [PROJECT REF]`. Function code:
```ts
import 'npm:@supabase/functions-js/edge-runtime.d.ts'
Deno.serve(async (req) => {
const data = await req.json()
console.log(`Received ${data.length} logs, first log:\n ${JSON.stringify(data[0])}`)
return new Response(JSON.stringify({ message: 'ok' }), {
headers: { 'Content-Type': 'application/json' },
})
})
```
Configure the drain with Gzip disabled, URL set to `https://[PROJECT REF].supabase.co/functions/v1/log-receiver`, and header `Authorization: Bearer [PUBLISHABLE KEY]`.
Edge Function as log drain feedback loop pitfall
Deploying an Edge Function as a log drain target will create a feedback loop—each drain event generates a new Edge Function log, which triggers another drain event. The batching behavior limits how fast this escalates, but it will run continuously.
Edge Function log message length limit
Edge Function log messages have a maximum length of 10,000 characters. Messages longer than this will be truncated.
Edge Functions report charts and metrics
The Edge Functions report includes: Total Edge Function Invocations (function response codes and error rates, shows function reliability and error patterns), Edge Function Execution Status Codes (function response codes and error rates, shows function reliability and error patterns), Edge Function Execution Time (average function duration and performance, shows performance optimization opportunities), Edge Function Invocations by Region (geographic distribution of function calls, shows global usage patterns and latency optimization).
Project permissions table by role - Edge Functions
Edge Functions permissions by role:
Update, Delete: Owner, Admin, and Developer.
View, List: Owner, Admin, Developer, and Read-Only.
Edge Function Invocations pricing model
Edge Function Invocations are billed using Package pricing, with each package representing 1 million invocations. If usage falls between two packages, you are billed for the next whole package.
Edge Function Invocations billing step example
Billing steps in 1 million invocation packages: 999,999 invocations = 1 package; 1,000,000 invocations = 1 package; 1,000,001 invocations = 2 packages; 1,500,000 invocations = 2 packages.
Edge Function Invocations invoice line item
Usage is shown as 'Function Invocations' on your invoice.
View Edge Function Invocations usage
Edge Function Invocations usage can be viewed on the organization's usage page at /dashboard/org/_/usage. The page shows usage of all projects by default. You can select a specific project from a dropdown and choose a different time period. The Edge Function Invocations section displays the number of invocations your projects have had during the selected time period.
Edge Function Invocations billing
You are charged for the number of times your functions get invoked, regardless of the response status code. Preflight (OPTIONS) requests are not billed.
Edge Functions egress definition and example
Edge Functions egress is data sent to the client when executing Edge Functions. Example: when a user completes a checkout process in an online shop and the client application triggers an Edge Function to process the payment, the confirmation response sent back to the client contributes to Edge Functions Egress.
Network restrictions impact on Edge Functions database access
With network restrictions applied, Edge Functions lose direct access to the database. To connect to the database from Edge Functions when network restrictions are in place, use supabase-js instead.
Reading messages from queue with Edge Function
To read messages from a Supabase Queue in an Edge Function, use the pgmq_public schema and call the 'read' RPC function with parameters: queue_name (string), sleep_seconds (number), and n (number for how many messages to read). Returns messages array or error object.
Triggering Edge Function queue processing
Edge Functions that consume queue messages can be scheduled to run periodically using Supabase Cron to process messages at regular intervals, or invoked on demand using supabase.functions.invoke().
Edge Function queue consumption example
Example Edge Function that reads 5 messages from a queue, processes them with custom logic, and deletes them on success:
```tsx
import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
import { createClient } from 'npm:@supabase/supabase-js@2'
const supabaseUrl = 'supabaseURL'
const supabaseKey = 'supabaseKey'
const supabase = createClient(supabaseUrl, supabaseKey)
const queueName = 'your_queue_name'
interface QueueMessage {
msg_id: bigint
read_ct: number
vt: string
enqueued_at: string
message: any
}
async function processMessage(message: QueueMessage) {
const { error: deleteError } = await supabase.schema('pgmq_public').rpc('delete', {
queue_name: queueName,
msg_id: message.msg_id,
})
if (deleteError) {
console.error(`Failed to delete message ${message.msg_id}:`, deleteError)
} else {
console.log(`Message ${message.msg_id} deleted from queue`)
}
}
Deno.serve(async (req) => {
const { data: messages, error } = await supabase.schema('pgmq_public').rpc('read', {
queue_name: queueName,
sleep_seconds: 0,
n: 5,
})
if (error) {
console.error(`Error reading from ${queueName} queue:`, error)
return new Response(JSON.stringify({ error: error.message }), {
status: 500,
headers: { 'Content-Type': 'application/json' },
})
}
if (!messages || messages.length === 0) {
console.log('No messages in workflow_messages queue')
return new Response(JSON.stringify({ message: 'No messages in queue' }), {
status: 200,
headers: { 'Content-Type': 'application/json' },
})
}
console.log(`Found ${messages.length} messages to process`)
for (const message of messages) {
try {
await processMessage(message as QueueMessage)
} catch (error) {
console.error(`Error processing message ${message.msg_id}:`, error)
}
}
return new Response(
JSON.stringify({
message: `Processing ${messages.length} messages in background`,
count: messages.length,
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
}
)
})
```
Edge Function queue message processing pattern
When an Edge Function processes queue messages, if processMessage throws an error, the message remains in the queue and will be read again on the next Edge Function execution. This enables retry logic for failed message processing.
Deleting messages from queue with Edge Function
To delete a message from a Supabase Queue, use the pgmq_public schema and call the 'delete' RPC function with parameters: queue_name (string) and msg_id (bigint). Messages should be deleted after successful processing.
Deno Edge Functions npm supply chain security
If using @supabase/supabase-js (or any npm: specifier) from Deno in a Supabase Edge Function, the npm-side minimum-release-age gate is unavailable at runtime. Mitigations: (1) pin to exact versions in import map / deno.json; avoid floating tags like latest. (2) Vendor critical dependencies with deno vendor and commit the vendored output to freeze the dep at a known-good snapshot and remove the runtime fetch. (3) Use --lock and --lock-write in CI to fail any build pulling unexpected content. (4) Stay current on Deno; newer versions land more supply-chain features (lockfile integrity, npm: provenance verification). Track the Deno release notes. Contact the Supabase Functions team if your security posture depends on a feature only in a newer Deno version.
Deploy Edge Functions
Use 'supabase functions deploy <function_name>' to deploy Edge Functions to your remote Supabase project.
Test Edge Function locally
Start the local functions server with: supabase functions serve. Then test with curl: curl --request POST 'http://localhost:54321/functions/v1/embed' --header 'Content-Type: application/json' --header 'apikey: SUP••••••EY' --data '{ "input": "hello world" }'. Replace SUPABASE_PUBLISHABLE_KEY with your project's publishable key from supabase status.
Generate text embeddings with Edge Functions
You can generate high quality text embeddings in Edge Functions using the built-in AI inference API without requiring an external API.
Create Edge Function for embeddings
Create an Edge Function using the command: supabase functions new embed. This creates a new TypeScript file called index.ts under ./supabase/functions/embed.
Example: Edge Functions error handling with instanceof
import { FunctionsFetchError, FunctionsHttpError, FunctionsRelayError } from '@supabase/supabase-js'; const { data, error } = await supabase.functions.invoke('hello'); if (error instanceof FunctionsHttpError) { console.error('Function error', await error.context.json()); } else if (error) { console.error(error); }
Edge Functions error handling
Functions errors arrive as one of three subclasses: FunctionsHttpError, FunctionsFetchError, or FunctionsRelayError. Narrow with instanceof; for FunctionsHttpError, parse the body to get the function's own error payload using await error.context.json().