Edge Functions

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:

1
Deno.serve(async (req) => {
2
const { method, url } = req
3
const { pathname } = new URL(url)
4
5
// Route based on method and path
6
if (method === 'GET' && pathname === '/users') {
7
return getAllUsers()
8
} else if (method === 'POST' && pathname === '/users') {
9
return createUser(req)
10
}
11
12
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.


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_environment
3
// This enables autocomplete, go to definition, etc.
4
5
import { createClient, SupabaseClient } from 'npm:supabase-js@2'
6
// New approach (v2.95.0+)
7
import { 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
// }
14
15
interface Task {
16
name: string
17
status: number
18
}
19
20
async function getTask(supabaseClient: SupabaseClient, id: string) {
21
const { data: task, error } = await supabaseClient.from('tasks').select('*').eq('id', id)
22
if (error) throw error
23
24
return new Response(JSON.stringify({ task }), {
25
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
26
status: 200,
27
})
28
}
29
30
async function getAllTasks(supabaseClient: SupabaseClient) {
31
const { data: tasks, error } = await supabaseClient.from('tasks').select('*')
32
if (error) throw error
33
34
return new Response(JSON.stringify({ tasks }), {
35
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
36
status: 200,
37
})
38
}
39
40
async function deleteTask(supabaseClient: SupabaseClient, id: string) {
41
const { error } = await supabaseClient.from('tasks').delete().eq('id', id)
42
if (error) throw error
43
44
return new Response(JSON.stringify({}), {
45
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
46
status: 200,
47
})
48
}
49
50
async function updateTask(supabaseClient: SupabaseClient, id: string, task: Task) {
51
const { error } = await supabaseClient.from('tasks').update(task).eq('id', id)
52
if (error) throw error
53
54
return new Response(JSON.stringify({ task }), {
55
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
56
status: 200,
57
})
58
}
59
60
async function createTask(supabaseClient: SupabaseClient, task: Task) {
61
const { error } = await supabaseClient.from('tasks').insert(task)
62
if (error) throw error
63
64
return new Response(JSON.stringify({ task }), {
65
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
66
status: 200,
67
})
68
}
69
70
Deno.serve(async (req) => {
71
const { url, method } = req
72
73
// 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
}
77
78
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
)
94
95
// For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
96
const taskPattern = new URLPattern({ pathname: '/restful-tasks/:id' })
97
const matchingPath = taskPattern.exec(url)
98
const id = matchingPath ? matchingPath.pathname.groups.id : null
99
100
let task = null
101
if (method === 'POST' || method === 'PUT') {
102
const body = await req.json()
103
task = body.task
104
}
105
106
// call relevant method based on method and id
107
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)
123
124
return new Response(JSON.stringify({ error: error.message }), {
125
headers: { ...corsHeaders, 'Content-Type': 'application/json' },
126
status: 400,
127
})
128
}
129
})
View source