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:
12345678910111213Deno.serve(async (req) => { const { method, url } = req const { pathname } = new URL(url) // Route based on method and path if (method === 'GET' && pathname === '/users') { return getAllUsers() } else if (method === 'POST' && pathname === '/users') { return createUser(req) } return new Response('Not found', { status: 404 })})
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.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126// Follow this setup guide to integrate the Deno language server with your editor:// https://deno.land/manual/getting_started/setup_your_environment// This enables autocomplete, go to definition, etc.import { createClient, SupabaseClient } from 'npm:supabase-js@2'const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey', 'Access-Control-Allow-Methods': 'POST, GET, OPTIONS, PUT, DELETE',}interface Task { name: string status: number}async function getTask(supabaseClient: SupabaseClient, id: string) { const { data: task, error } = await supabaseClient.from('tasks').select('*').eq('id', id) if (error) throw error return new Response(JSON.stringify({ task }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200, })}async function getAllTasks(supabaseClient: SupabaseClient) { const { data: tasks, error } = await supabaseClient.from('tasks').select('*') if (error) throw error return new Response(JSON.stringify({ tasks }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200, })}async function deleteTask(supabaseClient: SupabaseClient, id: string) { const { error } = await supabaseClient.from('tasks').delete().eq('id', id) if (error) throw error return new Response(JSON.stringify({}), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200, })}async function updateTask(supabaseClient: SupabaseClient, id: string, task: Task) { const { error } = await supabaseClient.from('tasks').update(task).eq('id', id) if (error) throw error return new Response(JSON.stringify({ task }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200, })}async function createTask(supabaseClient: SupabaseClient, task: Task) { const { error } = await supabaseClient.from('tasks').insert(task) if (error) throw error return new Response(JSON.stringify({ task }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200, })}Deno.serve(async (req) => { const { url, method } = req // This is needed if you're planning to invoke your function from a browser. if (method === 'OPTIONS') { return new Response('ok', { headers: corsHeaders }) } try { // Create a Supabase client with the Auth context of the logged in user. const supabaseClient = createClient( // Supabase API URL - env var exported by default. Deno.env.get('SUPABASE_URL') ?? '', // Supabase API ANON KEY - env var exported by default. Deno.env.get('SUPABASE_ANON_KEY') ?? '', // Create client with Auth context of the user that called the function. // This way your row-level-security (RLS) policies are applied. { global: { headers: { Authorization: req.headers.get('Authorization')! }, }, } ) // For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API const taskPattern = new URLPattern({ pathname: '/restful-tasks/:id' }) const matchingPath = taskPattern.exec(url) const id = matchingPath ? matchingPath.pathname.groups.id : null let task = null if (method === 'POST' || method === 'PUT') { const body = await req.json() task = body.task } // call relevant method based on method and id switch (true) { case id && method === 'GET': return getTask(supabaseClient, id as string) case id && method === 'PUT': return updateTask(supabaseClient, id as string, task) case id && method === 'DELETE': return deleteTask(supabaseClient, id as string) case method === 'POST': return createTask(supabaseClient, task) case method === 'GET': return getAllTasks(supabaseClient) default: return getAllTasks(supabaseClient) } } catch (error) { console.error(error) return new Response(JSON.stringify({ error: error.message }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 400, }) }})