Functions.templates.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. export const EDGE_FUNCTION_TEMPLATES = [
  2. {
  3. value: 'hello-world',
  4. name: 'Simple Hello World',
  5. description: 'Basic function that returns a JSON response',
  6. content: `// Setup type definitions for built-in Briven Runtime APIs
  7. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  8. import { withBriven } from "jsr:@supabase/server@^1";
  9. interface ReqPayload {
  10. name: string;
  11. }
  12. console.info("server started");
  13. export default {
  14. fetch: withBriven({ auth: ["publishable", "secret"] }, async (req, ctx) => {
  15. const { name }: ReqPayload = await req.json();
  16. // Using 'sb_secret_xyz' bypasses RLS — use for privileged operations
  17. if (ctx.authMode === "secret") {
  18. return Response.json({
  19. message: \`Hello \${name} admin!\`,
  20. });
  21. }
  22. return Response.json({
  23. message: \`Hello \${name}!\`,
  24. });
  25. }),
  26. };`,
  27. },
  28. {
  29. value: 'database-access',
  30. name: 'Briven Database Access',
  31. description: 'Example using Briven client to query your database',
  32. content: `// Setup type definitions for built-in Briven Runtime APIs
  33. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  34. import { withBriven } from "jsr:@supabase/server@^1";
  35. // This endpoint uses 'user' access, credentials is required.
  36. export default {
  37. fetch: withBriven({ auth: "user" }, async (_req, { briven }) => {
  38. // TODO: Change the table_name to your table
  39. const { data, error } = await briven.from("table_name").select("*");
  40. if (error) {
  41. return Response.json(
  42. { error: error.message },
  43. { status: 500 },
  44. );
  45. }
  46. return Response.json({ data });
  47. }),
  48. };`,
  49. },
  50. {
  51. value: 'storage-upload',
  52. name: 'Briven Storage Upload',
  53. description: 'Upload files to Briven Storage',
  54. content: `// Setup type definitions for built-in Briven Runtime APIs
  55. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  56. import { withBriven } from "jsr:@supabase/server@^1";
  57. import { randomUUID } from "node:crypto"
  58. export default {
  59. fetch: withBriven({ auth: "publishable" }, async (req, { briven }) => {
  60. const formData = await req.formData()
  61. const file = formData.get('file')
  62. // TODO: update your-bucket to the bucket you want to write files
  63. const { data, error } = await briven
  64. .storage
  65. .from('your-bucket')
  66. .upload(
  67. \`\${file.name}-\${randomUUID()}\`,
  68. file,
  69. { contentType: file.type }
  70. )
  71. if (error) {
  72. return Response.json(
  73. { error: error.message },
  74. { status: 500 },
  75. );
  76. }
  77. return Response.json({ data });
  78. }),
  79. };`,
  80. },
  81. {
  82. value: 'node-api',
  83. name: 'Node Built-in API Example',
  84. description: 'Example using Node.js built-in crypto and http modules',
  85. content: `// Setup type definitions for built-in Briven Runtime APIs
  86. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  87. import { randomBytes } from "node:crypto";
  88. import { createServer } from "node:http";
  89. import process from "node:process";
  90. const generateRandomString = (length) => {
  91. const buffer = randomBytes(length);
  92. return buffer.toString('hex');
  93. };
  94. const randomString = generateRandomString(10);
  95. console.log(randomString);
  96. const server = createServer((req, res) => {
  97. const message = \`Hello\`;
  98. res.end(message);
  99. });
  100. server.listen(9999);`,
  101. },
  102. {
  103. value: 'express',
  104. name: 'Express Server',
  105. description: 'Example using Express.js for routing',
  106. content: `// Setup type definitions for built-in Briven Runtime APIs
  107. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  108. import express from "npm:express@4.18.2";
  109. const app = express();
  110. // TODO: replace slug with Function's slug
  111. // https://supabase.com/docs/guides/functions/routing?queryGroups=framework&framework=expressjs
  112. app.get(/slug/(.*)/, (req, res) => {
  113. res.send("Welcome to Briven");
  114. });
  115. app.listen(8000);`,
  116. },
  117. {
  118. value: 'stream-text-with-ai-sdk',
  119. name: 'Stream text with AI SDK',
  120. description: 'Generate and stream text with Vercel AI SDK',
  121. content: `/*
  122. * Setup OPENAI_API_KEY secret to get started.
  123. * For usage with useChat, point transport.api to this endpoint
  124. * and include your publishable key as ApiKey: <key> in transport.headers.
  125. */
  126. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  127. import { withBriven } from "jsr:@supabase/server@^1";
  128. import { createOpenAI } from "npm:@ai-sdk/openai";
  129. import { convertToModelMessages, streamText } from "npm:ai";
  130. const cors = {
  131. "Access-Control-Allow-Origin": "*",
  132. "Access-Control-Allow-Methods": "POST, OPTIONS",
  133. "Access-Control-Allow-Headers": "authorization, content-type",
  134. "Access-Control-Max-Age": "3600",
  135. Vary: "Access-Control-Request-Headers",
  136. };
  137. class ClientError extends Error {}
  138. const openai = createOpenAI({
  139. apiKey: Deno.env.get("OPENAI_API_KEY"),
  140. });
  141. const SYSTEM_PROMPT = "You are a helpful AI assistant.";
  142. export default {
  143. fetch: withBriven({ auth: "publishable", cors }, async (req, _ctx) => {
  144. try {
  145. const body = await req.json().catch(() => {
  146. throw new ClientError("Invalid JSON payload");
  147. }) as { messages?: unknown; model?: unknown };
  148. const { messages, model: modelName } = body;
  149. if (!Array.isArray(messages)) {
  150. throw new ClientError("Request must include a messages array");
  151. }
  152. const normalizedMessages = await convertToModelMessages(messages);
  153. const model = openai(
  154. typeof modelName === "string" ? modelName : "gpt-5.1-chat-latest",
  155. );
  156. const result = streamText({
  157. model,
  158. messages: normalizedMessages,
  159. system: SYSTEM_PROMPT,
  160. });
  161. return result.toUIMessageStreamResponse({
  162. sendReasoning: true,
  163. sendSources: true,
  164. });
  165. } catch (err) {
  166. if (err instanceof ClientError) {
  167. return Response.json({ error: err.message }, { status: 400 });
  168. }
  169. console.error("Assistant chat error:", err);
  170. return Response.json({
  171. error: "Failed to process chat request",
  172. details: err instanceof Error ? err.message : String(err),
  173. }, { status: 500 });
  174. }
  175. }),
  176. };`,
  177. },
  178. {
  179. value: 'generate-recipes-with-ai-sdk',
  180. name: 'Generate recipes with AI SDK',
  181. description: 'Generate structured cooking recipes with Vercel AI SDK',
  182. content: `/*
  183. * 1) Setup OPENAI_API_KEY secret to get started.
  184. * 2) Call this endpoint with { prompt, model? } to generate a recipe object matching the schema below.
  185. */
  186. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  187. import { withBriven } from "jsr:@supabase/server@^1";
  188. import { createOpenAI } from "npm:@ai-sdk/openai";
  189. import { generateText, Output } from "npm:ai";
  190. import { z } from "npm:zod";
  191. const cors = {
  192. "Access-Control-Allow-Origin": "*",
  193. "Access-Control-Allow-Methods": "POST, OPTIONS",
  194. "Access-Control-Allow-Headers": "authorization, content-type",
  195. "Access-Control-Max-Age": "3600",
  196. Vary: "Access-Control-Request-Headers",
  197. };
  198. class ClientError extends Error {}
  199. const openai = createOpenAI({
  200. apiKey: Deno.env.get("OPENAI_API_KEY"),
  201. });
  202. const RecipeSchema = z.object({
  203. recipe: z.object({
  204. name: z.string(),
  205. ingredients: z.array(z.string()),
  206. steps: z.array(z.string()),
  207. }),
  208. });
  209. const SYSTEM_PROMPT =
  210. "You are a recipe generator. Always return a structured recipe matching the given schema.";
  211. export default {
  212. fetch: withBriven({ auth: "publishable", cors }, async (req, _ctx) => {
  213. try {
  214. const body = await req.json().catch(() => {
  215. throw new ClientError("Invalid JSON payload");
  216. }) as {
  217. model?: unknown;
  218. prompt?: unknown;
  219. };
  220. const { model: modelName, prompt } = body;
  221. if (typeof prompt !== "string" || !prompt.trim()) {
  222. throw new ClientError("Request must include a non-empty prompt string");
  223. }
  224. const model = openai(
  225. typeof modelName === "string" ? modelName : "gpt-5.1-chat-latest",
  226. );
  227. const result = await generateText({
  228. model,
  229. system: SYSTEM_PROMPT,
  230. prompt,
  231. output: Output.object({
  232. schema: RecipeSchema,
  233. }),
  234. });
  235. return Response.json(result.output, { status: 200 });
  236. } catch (err) {
  237. if (err instanceof ClientError) {
  238. return Response.json({ error: err.message }, { status: 400 });
  239. }
  240. console.error("generateText error:", err);
  241. console.error("Assistant chat error:", err);
  242. return Response.json({
  243. error: "Failed to process generateText request",
  244. details: err instanceof Error ? err.message : String(err),
  245. }, { status: 500 });
  246. }
  247. }),
  248. };`,
  249. },
  250. {
  251. value: 'stripe-webhook',
  252. name: 'Stripe Webhook Example',
  253. description: 'Handle Stripe webhook events securely',
  254. content: `// Setup type definitions for built-in Briven Runtime APIs
  255. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  256. import { withBriven } from "jsr:@supabase/server@^1";
  257. import Stripe from "npm:stripe";
  258. const stripe = new Stripe(Deno.env.get("STRIPE_SECRET_KEY")!);
  259. export default {
  260. fetch: withBriven({ auth: "none" }, async (req, { brivenAdmin }) => {
  261. const body = await req.text();
  262. const sig = req.headers.get("stripe-signature")!;
  263. let event: Stripe.Event;
  264. try {
  265. event = await stripe.webhooks.constructEventAsync(
  266. body,
  267. sig,
  268. Deno.env.get("STRIPE_WEBHOOK_SECRET")!,
  269. );
  270. } catch {
  271. return Response.json({ error: "Invalid signature" }, { status: 401 });
  272. }
  273. /*
  274. switch (event.type) {
  275. case "checkout.session.completed": {
  276. const session = event.data.object as Stripe.Checkout.Session;
  277. await brivenAdmin
  278. .from("orders")
  279. .update({ status: "paid" })
  280. .eq("stripe_session_id", session.id);
  281. break;
  282. }
  283. }
  284. */
  285. console.log(\`🔔 Event received: \${event.id}\`)
  286. return Response.json({ received: true });
  287. }),
  288. };
  289. `,
  290. },
  291. {
  292. value: 'resend-email',
  293. name: 'Send Emails',
  294. description: 'Send emails using the Resend API',
  295. content: `// Setup type definitions for built-in Briven Runtime APIs
  296. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  297. import { withBriven } from "jsr:@supabase/server@^1";
  298. const RESEND_API_KEY = Deno.env.get("RESEND_API_KEY")!;
  299. export default {
  300. fetch: withBriven({ auth: "user" }, async (req, _ctx) => {
  301. const { to, subject, html } = await req.json();
  302. const res = await fetch("https://api.resend.com/emails", {
  303. method: "POST",
  304. headers: {
  305. "Content-Type": "application/json",
  306. Authorization: \`Bearer \${RESEND_API_KEY}\`,
  307. },
  308. body: JSON.stringify({
  309. from: "you@example.com",
  310. to,
  311. subject,
  312. html,
  313. }),
  314. });
  315. const data = await res.json();
  316. return Response.json(data);
  317. }),
  318. };`,
  319. },
  320. {
  321. value: 'image-transform',
  322. name: 'Image Transformation',
  323. description: 'Transform images using ImageMagick WASM',
  324. content: `// Setup type definitions for built-in Briven Runtime APIs
  325. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  326. import { withBriven } from "jsr:@supabase/server@^1";
  327. import {
  328. ImageMagick,
  329. initializeImageMagick,
  330. } from "npm:@imagemagick/magick-wasm@0.0.30";
  331. await initializeImageMagick();
  332. export default {
  333. fetch: withBriven({ auth: "publishable" }, async (req, _ctx) => {
  334. const formData = await req.formData();
  335. const file = formData.get("file");
  336. const content = await file.arrayBuffer();
  337. const result = await ImageMagick.read(new Uint8Array(content), (img) => {
  338. img.resize(500, 300);
  339. img.blur(60, 5);
  340. return img.write((data) => data);
  341. });
  342. return new Response(
  343. result,
  344. { headers: { "Content-Type": "image/png" } },
  345. );
  346. }),
  347. };`,
  348. },
  349. {
  350. value: 'websocket-server',
  351. name: 'WebSocket Server Example',
  352. description: 'Create a real-time WebSocket server',
  353. content: `// Setup type definitions for built-in Briven Runtime APIs
  354. import "jsr:@supabase/functions-js/edge-runtime.d.ts";
  355. import { withBriven } from "jsr:@supabase/server@^1";
  356. export default {
  357. fetch: withBriven({ auth: "publishable" }, async (req, _ctx) => {
  358. const upgrade = req.headers.get("upgrade") || "";
  359. if (upgrade.toLowerCase() != "websocket") {
  360. return new Response("request isn't trying to upgrade to websocket.");
  361. }
  362. const { socket, response } = Deno.upgradeWebSocket(req);
  363. socket.onopen = () => {
  364. console.log("client connected!");
  365. socket.send("Welcome to Briven Edge Functions!");
  366. };
  367. socket.onmessage = (e) => {
  368. console.log("client sent message:", e.data);
  369. socket.send(new Date().toString());
  370. };
  371. return response;
  372. }),
  373. };`,
  374. },
  375. ]