Skip to content
Edge Functions

在函数中处理路由

Handle custom routing within Edge Functions.

通常,Edge Function 是用来执行单一操作的(例如,把一条记录写入数据库)。不过,如果你的应用逻辑分成了多个 Edge Function,每个操作的请求可能会显得更慢。

🌐 Usually, an Edge Function is written to perform a single action (e.g. write a record to the database). However, if your app's logic is split into multiple Edge Functions, requests to each action may seem slower.

每个边缘函数在响应请求前都需要启动(称为冷启动)。如果某个操作执行得不太频繁(例如删除记录),那么这个函数很可能会遇到冷启动。

🌐 Each Edge Function needs to be booted before serving a request (known as cold starts). If an action is performed less frequently (e.g. deleting a record), there is a high chance of that function experiencing a cold start.

减少冷启动并提高性能的一种方法是将多个动作合并到一个 Edge Function 中。这样只需要启动一个实例,它就可以处理针对不同动作的多个请求。

🌐 One way to reduce cold starts and increase performance is to combine multiple actions into a single Edge Function. This way only one instance needs to be booted and it can handle multiple requests to different actions.

这让你可以:

🌐 This allows you to:

  • 通过把多个操作合并成一个函数来减少冷启动
  • 在一个函数中构建完整的 REST API
  • 通过为多个端点保持一个实例处于活跃状态来提升性能

例如,我们可以使用一个 Edge Function 来创建一个典型的 CRUD API(创建、读取、更新、删除记录)。

🌐 For example, we can use a single Edge Function to create a typical CRUD API (create, read, update, delete records).

要将多个端点组合到一个 Edge Function 中,你可以使用像 ExpressOakHono 这样的网络应用框架。

🌐 To combine multiple endpoints into a single Edge Function, you can use web application frameworks such as Express, Oak, or Hono.


基础路由示例 #

🌐 Basic routing example

这里有一个使用一些流行的网页框架的基本 Hello World 示例:

🌐 Here's a basic hello world example using some popular web frameworks:

1
import { Hono } from 'jsr:@hono/hono@^4'
2
3
const app = new Hono()
4
5
app.post('/hello-world', async (c) => {
6
const { name } = await c.req.json()
7
return c.json({ message: `Hello ${name}!` })
8
})
9
10
app.get('/hello-world', (c) => {
11
return c.json({ message: 'Hello World!' })
12
})
13
14
export default { fetch: app.fetch }

要为每个路由添加 Supabase 认证,可以使用来自 npm:@supabase/server@^1/adapters/hono 的 Hono 适配器。查看 保护边缘函数

🌐 To add Supabase auth per route, use the Hono adapter from npm:@supabase/server@^1/adapters/hono. See Securing Edge Functions.


使用路由参数 #

🌐 Using route parameters

你可以使用路由参数来获取特定 URL 段的值(例如 /tasks/:taskId/notes/:noteId)。

🌐 You can use route parameters to capture values at specific URL segments (e.g. /tasks/:taskId/notes/:noteId).

记住,路径必须以函数名为前缀。路由参数只能在函数名前缀之后使用。

🌐 Keep in mind paths must be prefixed by function name. Route parameters can only be used after the function name prefix.

1
import { withSupabase } from 'npm:@supabase/server@^1'
2
3
interface Task {
4
id: string
5
name: string
6
}
7
8
let tasks: Task[] = []
9
10
const router = new Map<string, (req: Request) => Promise<Response>>()
11
12
async function getAllTasks(): Promise<Response> {
13
return Response.json({ tasks })
14
}
15
16
async function getTask(id: string): Promise<Response> {
17
const task = tasks.find((t) => t.id === id)
18
if (task) {
19
return Response.json({ task })
20
} else {
21
return Response.json({ error: 'Task not found' }, { status: 404 })
22
}
23
}
24
25
async function createTask(req: Request): Promise<Response> {
26
const id = Math.random().toString(36).substring(7)
27
const task = { id, name: '' }
28
tasks.push(task)
29
return Response.json({ task }, { status: 201 })
30
}
31
32
async function updateTask(id: string, req: Request): Promise<Response> {
33
const index = tasks.findIndex((t) => t.id === id)
34
if (index !== -1) {
35
const updates = await req.json()
36
tasks[index] = { ...tasks[index], ...updates }
37
return Response.json({ task: tasks[index] })
38
} else {
39
return Response.json({ error: 'Task not found' }, { status: 404 })
40
}
41
}
42
43
async function deleteTask(id: string): Promise<Response> {
44
const index = tasks.findIndex((t) => t.id === id)
45
if (index !== -1) {
46
tasks.splice(index, 1)
47
return Response.json({ message: 'Task deleted successfully' })
48
} else {
49
return Response.json({ error: 'Task not found' }, { status: 404 })
50
}
51
}
52
53
export default {
54
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
55
const url = new URL(req.url)
56
const method = req.method
57
// Extract the last part of the path as the command
58
const command = url.pathname.split('/').pop()
59
// Assuming the last part of the path is the task ID
60
const id = command
61
try {
62
switch (method) {
63
case 'GET':
64
if (id) {
65
return getTask(id)
66
} else {
67
return getAllTasks()
68
}
69
case 'POST':
70
return createTask(req)
71
case 'PUT':
72
if (id) {
73
return updateTask(id, req)
74
} else {
75
return Response.json({ error: 'Bad Request' }, { status: 400 })
76
}
77
case 'DELETE':
78
if (id) {
79
return deleteTask(id)
80
} else {
81
return Response.json({ error: 'Bad Request' }, { status: 400 })
82
}
83
default:
84
return Response.json({ error: 'Method Not Allowed' }, { status: 405 })
85
}
86
} catch (error) {
87
return Response.json({ error: `Internal Server Error: ${error}` }, { status: 500 })
88
}
89
}),
90
}

URL 模式 API #

🌐 URL Patterns API

如果你不想使用网络框架,你可以直接在 Edge Functions 中使用 URL Pattern API 来实现路由。

🌐 If you prefer not to use a web framework, you can directly use URL Pattern API within your Edge Functions to implement routing.

这对于只有几个路由的小型应用效果很好:

🌐 This works well for small apps with only a couple of routes:

1
// ...
2
3
export default {
4
fetch: withSupabase({ auth: 'user' }, async (req, ctx) => {
5
const { url, method } = req
6
7
try {
8
// ctx.supabase is scoped to the calling user, so your row-level-security
9
// (RLS) policies are applied.
10
const supabaseClient = ctx.supabase
11
12
// For more details on URLPattern, check https://developer.mozilla.org/en-US/docs/Web/API/URL_Pattern_API
13
const taskPattern = new URLPattern({ pathname: '/restful-tasks/:id' })
14
const matchingPath = taskPattern.exec(url)
15
const id = matchingPath ? matchingPath.pathname.groups.id : null
16
17
let task = null
18
if (method === 'POST' || method === 'PUT') {
19
const body = await req.json()
20
task = body.task
21
}
22
23
// call relevant method based on method and id
24
switch (true) {
25
case id && method === 'GET':
26
return getTask(supabaseClient, id as string)
27
case id && method === 'PUT':
28
return updateTask(supabaseClient, id as string, task)
29
case id && method === 'DELETE':
30
return deleteTask(supabaseClient, id as string)
31
case method === 'POST':
32
return createTask(supabaseClient, task)
33
case method === 'GET':
34
return getAllTasks(supabaseClient)
35
default:
36
return getAllTasks(supabaseClient)
37
}
38
} catch (error) {
39
console.error(error)
40
41
return Response.json({ error: error.message }, { status: 400 })
42
}
43
}),
44
}
View source