Files
supabase/apps/studio/components/interfaces/Functions/Functions.templates.ts
18cc6f8fe9 Replace Edge Functions legacy OpenAI template with two new AI SDK-based templates (#40904)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Edge Functions templates update 

## What is the current behavior?

An OpenAI template using GPT-3 and the legacy completions API.

## What is the new behavior?

Removed

- OpenAI text completion template

Added

- Stream text with AI SDK template
- Generate recipes with AI SDK template

## Additional context

- CORS inline as examples do not support multiple files yet
- Tested both by direct invocation and via `useChat`


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added "Stream text with AI SDK" template for real-time streaming
responses with CORS support and robust error handling.
* Added "Generate recipes with AI SDK" template for producing validated,
structured recipe outputs.

* **Refactor**
* Replaced the previous OpenAI text completion template with two new AI
SDK–based templates to provide streaming and schema-driven content
generation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Kalleby Santos <kallebysantos@Kallebys-MacBook-Pro.local>
Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2026-02-09 12:26:01 +08:00

433 lines
12 KiB
TypeScript

export const EDGE_FUNCTION_TEMPLATES = [
{
value: 'hello-world',
name: 'Simple Hello World',
description: 'Basic function that returns a JSON response',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
interface reqPayload {
name: string;
}
console.info('server started');
Deno.serve(async (req: Request) => {
const { name }: reqPayload = await req.json();
const data = {
message: \`Hello \${name}!\`,
};
return new Response(
JSON.stringify(data),
{ headers: { 'Content-Type': 'application/json', 'Connection': 'keep-alive' }}
);
});`,
},
{
value: 'database-access',
name: 'Supabase Database Access',
description: 'Example using Supabase client to query your database',
content: `// 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'
Deno.serve(async (req) => {
try {
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? '',
{ global: { headers: { Authorization: req.headers.get('Authorization')! } } }
)
// TODO: Change the table_name to your table
const { data, error } = await supabase.from('table_name').select('*')
if (error) {
throw error
}
return new Response(JSON.stringify({ data }), {
headers: { 'Content-Type': 'application/json' },
status: 200,
})
} catch (err) {
return new Response(JSON.stringify({ message: err?.message ?? err }), {
headers: { 'Content-Type': 'application/json' },
status: 500
})
}
})`,
},
{
value: 'storage-upload',
name: 'Supabase Storage Upload',
description: 'Upload files to Supabase Storage',
content: `// 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 { randomUUID } from 'node:crypto'
const supabase = createClient(
Deno.env.get('SUPABASE_URL') ?? '',
Deno.env.get('SUPABASE_ANON_KEY') ?? ''
)
Deno.serve(async (req) => {
const formData = await req.formData()
const file = formData.get('file')
// TODO: update your-bucket to the bucket you want to write files
const { data, error } = await supabase
.storage
.from('your-bucket')
.upload(
\`\${file.name}-\${randomUUID()}\`,
file,
{ contentType: file.type }
)
if (error) throw error
return new Response(
JSON.stringify({ data }),
{ headers: { 'Content-Type': 'application/json' }}
)
})`,
},
{
value: 'node-api',
name: 'Node Built-in API Example',
description: 'Example using Node.js built-in crypto and http modules',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import { randomBytes } from "node:crypto";
import { createServer } from "node:http";
import process from "node:process";
const generateRandomString = (length) => {
const buffer = randomBytes(length);
return buffer.toString('hex');
};
const randomString = generateRandomString(10);
console.log(randomString);
const server = createServer((req, res) => {
const message = \`Hello\`;
res.end(message);
});
server.listen(9999);`,
},
{
value: 'express',
name: 'Express Server',
description: 'Example using Express.js for routing',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import express from "npm:express@4.18.2";
const app = express();
// TODO: replace slug with Function's slug
// https://supabase.com/docs/guides/functions/routing?queryGroups=framework&framework=expressjs
app.get(/slug/(.*)/, (req, res) => {
res.send("Welcome to Supabase");
});
app.listen(8000);`,
},
{
value: 'stream-text-with-ai-sdk',
name: 'Stream text with AI SDK',
description: 'Generate and stream text with Vercel AI SDK',
content: `/*
* Setup OPENAI_API_KEY secret to get started.
* For usage with useChat, point transport.api to this endpoint
* and include your publishable key as Authorization: Bearer <key> in transport.headers.
*/
import { createOpenAI } from 'npm:@ai-sdk/openai';
import { convertToModelMessages, streamText } from 'npm:ai';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'authorization, content-type',
'Access-Control-Max-Age': '3600',
Vary: 'Access-Control-Request-Headers',
};
const json = (status: number, body: unknown) =>
new Response(JSON.stringify(body), {
status,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
class ClientError extends Error {}
const openai = createOpenAI({
apiKey: Deno.env.get('OPENAI_API_KEY'),
});
const SYSTEM_PROMPT = 'You are a helpful AI assistant.';
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders });
}
try {
const body = await req.json().catch(() => {
throw new ClientError('Invalid JSON payload');
}) as { messages?: unknown; model?: unknown };
const { messages, model: modelName } = body;
if (!Array.isArray(messages)) {
throw new ClientError('Request must include a messages array');
}
const normalizedMessages = await convertToModelMessages(messages);
const model = openai(
typeof modelName === 'string' ? modelName : 'gpt-5.1-chat-latest',
);
const result = streamText({
model,
messages: normalizedMessages,
system: SYSTEM_PROMPT,
});
return result.toUIMessageStreamResponse({
headers: corsHeaders,
sendReasoning: true,
sendSources: true,
});
} catch (err) {
if (err instanceof ClientError) {
return json(400, { error: err.message });
}
console.error('Assistant chat error:', err);
return json(500, {
error: 'Failed to process chat request',
details: err instanceof Error ? err.message : String(err),
});
}
});`,
},
{
value: 'generate-recipes-with-ai-sdk',
name: 'Generate recipes with AI SDK',
description: 'Generate structured cooking recipes with Vercel AI SDK',
content: `/*
* 1) Setup OPENAI_API_KEY secret to get started.
* 2) Call this endpoint with { prompt, model? } to generate a recipe object matching the schema below.
*/
import { createOpenAI } from 'npm:@ai-sdk/openai';
import { generateText, Output } from "npm:ai";
import { z } from 'npm:zod';
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'POST, OPTIONS',
'Access-Control-Allow-Headers': 'authorization, content-type',
'Access-Control-Max-Age': '3600',
Vary: 'Access-Control-Request-Headers',
};
const json = (status: number, body: unknown) =>
new Response(JSON.stringify(body), {
status,
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
});
class ClientError extends Error {}
const openai = createOpenAI({
apiKey: Deno.env.get('OPENAI_API_KEY'),
});
const RecipeSchema = z.object({
recipe: z.object({
name: z.string(),
ingredients: z.array(z.string()),
steps: z.array(z.string()),
}),
});
const SYSTEM_PROMPT =
'You are a recipe generator. Always return a structured recipe matching the given schema.';
Deno.serve(async (req) => {
if (req.method === 'OPTIONS') {
return new Response('ok', { headers: corsHeaders });
}
try {
const body = await req.json().catch(() => {
throw new ClientError('Invalid JSON payload');
}) as {
model?: unknown;
prompt?: unknown;
};
const { model: modelName, prompt } = body;
if (typeof prompt !== 'string' || !prompt.trim()) {
throw new ClientError('Request must include a non-empty prompt string');
}
const model = openai(
typeof modelName === 'string' ? modelName : 'gpt-5.1-chat-latest',
);
const result = await generateText({
model,
system: SYSTEM_PROMPT,
prompt,
output: Output.object({
schema: RecipeSchema,
}),
});
return json(200, result.output);
} catch (err) {
if (err instanceof ClientError) {
return json(400, { error: err.message });
}
console.error('generateText error:', err);
return json(500, {
error: 'Failed to process generateText request',
details: err instanceof Error ? err.message : String(err),
});
}
});`,
},
{
value: 'stripe-webhook',
name: 'Stripe Webhook Example',
description: 'Handle Stripe webhook events securely',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import Stripe from 'npm:stripe@12.0.0'
const stripe = new Stripe(Deno.env.get('STRIPE_API_KEY') as string, {
// This is needed to use the Fetch API rather than relying on the Node http
// package.
apiVersion: '2024-11-20'
})
// This is needed in order to use the Web Crypto API in Deno.
const cryptoProvider = Stripe.createSubtleCryptoProvider()
console.log('Stripe Webhook Function booted!')
Deno.serve(async (request) => {
const signature = request.headers.get('Stripe-Signature')
// First step is to verify the event. The .text() method must be used as the
// verification relies on the raw request body rather than the parsed JSON.
const body = await request.text()
let receivedEvent
try {
receivedEvent = await stripe.webhooks.constructEventAsync(
body,
signature!,
Deno.env.get('STRIPE_WEBHOOK_SIGNING_SECRET')!,
undefined,
cryptoProvider
)
} catch (err) {
return new Response(err.message, { status: 400 })
}
console.log(\`🔔 Event received: \${receivedEvent.id}\`)
return new Response(JSON.stringify({ ok: true }), { status: 200 })
});`,
},
{
value: 'resend-email',
name: 'Send Emails',
description: 'Send emails using the Resend API',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
const RESEND_API_KEY = Deno.env.get('RESEND_API_KEY')!
Deno.serve(async (req) => {
const { to, subject, html } = await req.json()
const res = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: \`Bearer \${RESEND_API_KEY}\`,
},
body: JSON.stringify({
from: 'you@example.com',
to,
subject,
html,
}),
})
const data = await res.json()
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' },
})
})`,
},
{
value: 'image-transform',
name: 'Image Transformation',
description: 'Transform images using ImageMagick WASM',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
import {
ImageMagick,
initializeImageMagick,
} from "npm:@imagemagick/magick-wasm@0.0.30"
await initializeImageMagick()
Deno.serve(async (req) => {
const formData = await req.formData()
const file = formData.get('file')
const content = await file.arrayBuffer()
const result = await ImageMagick.read(new Uint8Array(content), (img) => {
img.resize(500, 300)
img.blur(60, 5)
return img.write(data => data)
})
return new Response(
result,
{ headers: { 'Content-Type': 'image/png' }}
)
})`,
},
{
value: 'websocket-server',
name: 'Websocket Server Example',
description: 'Create a real-time WebSocket server',
content: `// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts";
Deno.serve((req) => {
const upgrade = req.headers.get("upgrade") || ""
if (upgrade.toLowerCase() != "websocket") {
return new Response("request isn't trying to upgrade to websocket.")
}
const { socket, response } = Deno.upgradeWebSocket(req)
socket.onopen = () => {
console.log("client connected!")
socket.send('Welcome to Supabase Edge Functions!')
}
socket.onmessage = (e) => {
console.log("client sent message:", e.data)
socket.send(new Date().toString())
}
return response
})`,
},
]