rembrembdocs

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    // Create a Supabase client with the Auth context of the logged in user.80    const supabaseClient = createClient(81      // Supabase API URL - env var exported by default.82      Deno.env.get('SUPABASE_URL') ?? '',83      // Supabase API ANON KEY - env var exported by default.84      Deno.env.get('SUPABASE_ANON_KEY') ?? '',85      // Create client with Auth context of the user that called the function.86      // This way your row-level-security (RLS) policies are applied.87      {88        global: {89          headers: { Authorization: req.headers.get('Authorization')! },90        },91      }92    )9394    // For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API95    const taskPattern = new URLPattern({ pathname: '/restful-tasks/:id' })96    const matchingPath = taskPattern.exec(url)97    const id = matchingPath ? matchingPath.pathname.groups.id : null9899    let task = null100    if (method === 'POST' || method === 'PUT') {101      const body = await req.json()102      task = body.task103    }104105    // call relevant method based on method and id106    switch (true) {107      case id && method === 'GET':108        return getTask(supabaseClient, id as string)109      case id && method === 'PUT':110        return updateTask(supabaseClient, id as string, task)111      case id && method === 'DELETE':112        return deleteTask(supabaseClient, id as string)113      case method === 'POST':114        return createTask(supabaseClient, task)115      case method === 'GET':116        return getAllTasks(supabaseClient)117      default:118        return getAllTasks(supabaseClient)119    }120  } catch (error) {121    console.error(error)122123    return new Response(JSON.stringify({ error: error.message }), {124      headers: { ...corsHeaders, 'Content-Type': 'application/json' },125      status: 400,126    })127  }128})

View source