Routing
Handle different request types in a single function to create efficient APIs.
Overview#
Edge Functions support GET, POST, PUT, PATCH, DELETE, and OPTIONS. This means you can build complete REST APIs in a single function:
1Deno.serve(async (req) => {2 const { method, url } = req3 const { pathname } = new URL(url)45 // Route based on method and path6 if (method === 'GET' && pathname === '/users') {7 return getAllUsers()8 } else if (method === 'POST' && pathname === '/users') {9 return createUser(req)10 }1112 return new Response('Not found', { status: 404 })13})Edge Functions allow you to build APIs without needing separate functions for each endpoint. This reduces cold starts and simplifies deployment while keeping your code organized.
HTML content is not supported. GET requests that return text/html will be rewritten to text/plain. Edge Functions are designed for APIs and data processing, not serving web pages. Use Supabase for your backend API and your favorite frontend framework for HTML.
Example#
Here's a full example of a RESTful API built with Edge Functions.
1// Follow this setup guide to integrate the Deno language server with your editor:2// https://deno.land/manual/getting_started/setup_your_environment3// This enables autocomplete, go to definition, etc.45import { createClient, SupabaseClient } from 'npm:supabase-js@2'6// New approach (v2.95.0+)7import { corsHeaders } from 'jsr:@supabase/supabase-js@2/cors'8// For older versions, use hardcoded headers:9// const corsHeaders = {10// 'Access-Control-Allow-Origin': '*',11// 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',12// 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',13// }1415interface Task {16 name: string17 status: number18}1920async function getTask(supabaseClient: SupabaseClient, id: string) {21 const { data: task, error } = await supabaseClient.from('tasks').select('*').eq('id', id)22 if (error) throw error2324 return new Response(JSON.stringify({ task }), {25 headers: { ...corsHeaders, 'Content-Type': 'application/json' },26 status: 200,27 })28}2930async function getAllTasks(supabaseClient: SupabaseClient) {31 const { data: tasks, error } = await supabaseClient.from('tasks').select('*')32 if (error) throw error3334 return new Response(JSON.stringify({ tasks }), {35 headers: { ...corsHeaders, 'Content-Type': 'application/json' },36 status: 200,37 })38}3940async function deleteTask(supabaseClient: SupabaseClient, id: string) {41 const { error } = await supabaseClient.from('tasks').delete().eq('id', id)42 if (error) throw error4344 return new Response(JSON.stringify({}), {45 headers: { ...corsHeaders, 'Content-Type': 'application/json' },46 status: 200,47 })48}4950async function updateTask(supabaseClient: SupabaseClient, id: string, task: Task) {51 const { error } = await supabaseClient.from('tasks').update(task).eq('id', id)52 if (error) throw error5354 return new Response(JSON.stringify({ task }), {55 headers: { ...corsHeaders, 'Content-Type': 'application/json' },56 status: 200,57 })58}5960async function createTask(supabaseClient: SupabaseClient, task: Task) {61 const { error } = await supabaseClient.from('tasks').insert(task)62 if (error) throw error6364 return new Response(JSON.stringify({ task }), {65 headers: { ...corsHeaders, 'Content-Type': 'application/json' },66 status: 200,67 })68}6970Deno.serve(async (req) => {71 const { url, method } = req7273 // This is needed if you're planning to invoke your function from a browser.74 if (method === 'OPTIONS') {75 return new Response('ok', { headers: corsHeaders })76 }7778 try {79 const SUPABASE_PUBLISHABLE_KEYS = JSON.parse(Deno.env.get('SUPABASE_PUBLISHABLE_KEYS')!)80 // Create a Supabase client with the Auth context of the logged in user.81 const supabaseClient = createClient(82 // Supabase API URL - env var exported by default.83 Deno.env.get('SUPABASE_URL') ?? '',84 // Supabase publishable key - env var exported by default.85 Deno.env.get(SUPABASE_PUBLISHABLE_KEYS['default']) ?? '',86 // Create client with Auth context of the user that called the function.87 // This way your row-level-security (RLS) policies are applied.88 {89 global: {90 headers: { Authorization: req.headers.get('Authorization')! },91 },92 }93 )9495 // For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API96 const taskPattern = new URLPattern({ pathname: '/restful-tasks/:id' })97 const matchingPath = taskPattern.exec(url)98 const id = matchingPath ? matchingPath.pathname.groups.id : null99100 let task = null101 if (method === 'POST' || method === 'PUT') {102 const body = await req.json()103 task = body.task104 }105106 // call relevant method based on method and id107 switch (true) {108 case id && method === 'GET':109 return getTask(supabaseClient, id as string)110 case id && method === 'PUT':111 return updateTask(supabaseClient, id as string, task)112 case id && method === 'DELETE':113 return deleteTask(supabaseClient, id as string)114 case method === 'POST':115 return createTask(supabaseClient, task)116 case method === 'GET':117 return getAllTasks(supabaseClient)118 default:119 return getAllTasks(supabaseClient)120 }121 } catch (error) {122 console.error(error)123124 return new Response(JSON.stringify({ error: error.message }), {125 headers: { ...corsHeaders, 'Content-Type': 'application/json' },126 status: 400,127 })128 }129})