API 路由是 TanStack Start 的一项强大功能,允许你在应用程序中创建服务端端点,而无需单独的服务器。API 路由对于处理表单提交、用户身份验证等非常有用。
默认情况下,API 路由在项目的 ./app/routes/api 目录中定义,并由 TanStack Start 服务器自动处理。
🧠 这意味着默认情况下,你的 API 路由将以 /api 为前缀,并与你的应用程序在同一服务器上提供。你可以通过更改 TanStack Start 配置中的 server.apiBaseURL 来自定义此基本路径。
TanStack Start 中的 API 路由遵循与 TanStack Router 相同的文件路由约定。这意味着你的 routes 目录中以 api 为前缀(可以配置)的每个文件都将被视为 API 路由。以下是一些示例
你的以 api 为前缀的路由文件,可以被认为是给定 API 路由路径的处理器。
重要的是要记住,每个路由只能有一个关联的处理器文件。因此,如果你有一个名为 routes/api/users.ts 的文件,它等于请求路径 /api/users,你不能有其他也解析到同一路由的文件,例如
❗ 还有一件事,API 路由没有无路径布局路由或并行路由的概念。因此,名为
在上面的示例中,你可能已经注意到文件命名约定很灵活,允许你混合和匹配目录和文件名。这是故意的,允许你以对你的应用程序有意义的方式组织你的 API 路由。你可以在 TanStack Router 基于文件的路由指南中阅读更多相关信息。
在你可以创建 API 路由之前,你需要为你的 TanStack Start 项目设置入口处理器。这个入口处理器,类似于 client 和 ssr,处理传入的 API 请求并将它们路由到适当的 API 路由处理器。API 入口处理器在你的项目的 app/api.ts 文件中定义。
这是一个示例实现
// app/api.ts
import {
createStartAPIHandler,
defaultAPIFileRouteHandler,
} from '@tanstack/react-start/api'
export default createStartAPIHandler(defaultAPIFileRouteHandler)
// app/api.ts
import {
createStartAPIHandler,
defaultAPIFileRouteHandler,
} from '@tanstack/react-start/api'
export default createStartAPIHandler(defaultAPIFileRouteHandler)
此文件负责创建 API 处理器,该处理器将用于将传入请求路由到适当的 API 路由处理器。defaultAPIFileRouteHandler 是一个辅助函数,它将根据传入请求自动加载和执行适当的 API 路由处理器。
API 路由通过调用 createAPIFileRoute 函数导出 APIRoute 实例。与 TanStack Router 中的其他基于文件的路由类似,此函数的第一个参数是路由的路径。返回的函数再次被调用,并带有一个对象,该对象定义了每个 HTTP 方法的路由处理器。
提示
如果你已经运行了开发服务器,当你创建一个新的 API 路由时,它将自动为你设置初始处理器。从那时起,你可以根据需要自定义处理器。
注意
导出变量必须命名为 APIRoute,否则结果响应将是 404 not found。
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response('Hello, World! from ' + request.url)
},
})
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response('Hello, World! from ' + request.url)
},
})
每个 HTTP 方法处理器都会收到一个具有以下属性的对象
一旦你处理了请求,你需要返回一个 Response 对象或 Promise<Response>。这可以通过创建一个新的 Response 对象并从处理器返回它来完成。你可以在 MDN Web 文档中阅读有关 Response 对象的更多信息。
API 路由支持动态路径参数,动态路径参数用 $ 后跟参数名称表示。例如,名为 routes/api/users/$id.ts 的文件将在 /api/users/$id 创建一个 API 路由,该路由接受动态 id 参数。
// routes/api/users/$id.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ params }) => {
const { id } = params
return new Response(`User ID: ${id}`)
},
})
// Visit /api/users/123 to see the response
// User ID: 123
// routes/api/users/$id.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ params }) => {
const { id } = params
return new Response(`User ID: ${id}`)
},
})
// Visit /api/users/123 to see the response
// User ID: 123
你还可以在单个路由中包含多个动态路径参数。例如,名为 routes/api/users/$id/posts/$postId.ts 的文件将在 /api/users/$id/posts/$postId 创建一个 API 路由,该路由接受两个动态参数。
// routes/api/users/$id/posts/$postId.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id/posts/$postId')({
GET: async ({ params }) => {
const { id, postId } = params
return new Response(`User ID: ${id}, Post ID: ${postId}`)
},
})
// Visit /api/users/123/posts/456 to see the response
// User ID: 123, Post ID: 456
// routes/api/users/$id/posts/$postId.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id/posts/$postId')({
GET: async ({ params }) => {
const { id, postId } = params
return new Response(`User ID: ${id}, Post ID: ${postId}`)
},
})
// Visit /api/users/123/posts/456 to see the response
// User ID: 123, Post ID: 456
API 路由还支持路径末尾的通配符参数,通配符参数用 $ 后跟空内容表示。例如,名为 routes/api/file/$.ts 的文件将在 /api/file/$ 创建一个 API 路由,该路由接受通配符参数。
// routes/api/file/$.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/file/$')({
GET: async ({ params }) => {
const { _splat } = params
return new Response(`File: ${_splat}`)
},
})
// Visit /api/file/hello.txt to see the response
// File: hello.txt
// routes/api/file/$.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/file/$')({
GET: async ({ params }) => {
const { _splat } = params
return new Response(`File: ${_splat}`)
},
})
// Visit /api/file/hello.txt to see the response
// File: hello.txt
要处理 POST 请求,你可以向路由对象添加一个 POST 处理器。处理器将接收请求对象作为第一个参数,你可以使用 request.json() 方法访问请求体。
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
POST: async ({ request }) => {
const body = await request.json()
return new Response(`Hello, ${body.name}!`)
},
})
// Send a POST request to /api/hello with a JSON body like { "name": "Tanner" }
// Hello, Tanner!
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
POST: async ({ request }) => {
const body = await request.json()
return new Response(`Hello, ${body.name}!`)
},
})
// Send a POST request to /api/hello with a JSON body like { "name": "Tanner" }
// Hello, Tanner!
这也适用于其他 HTTP 方法,如 PUT、PATCH 和 DELETE。你可以在路由对象中为这些方法添加处理器,并使用适当的方法访问请求体。
重要的是要记住,request.json() 方法返回一个 Promise,它解析为请求的已解析 JSON 体。你需要 await 结果才能访问请求体。
这是在 API 路由中处理 POST 请求的常见模式。你还可以使用其他方法,如 request.text() 或 request.formData() 来访问请求体。
当使用 Response 对象返回 JSON 时,这是一种常见模式
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response(JSON.stringify({ message: 'Hello, World!' }), {
headers: {
'Content-Type': 'application/json',
},
})
},
})
// Visit /api/hello to see the response
// {"message":"Hello, World!"}
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response(JSON.stringify({ message: 'Hello, World!' }), {
headers: {
'Content-Type': 'application/json',
},
})
},
})
// Visit /api/hello to see the response
// {"message":"Hello, World!"}
或者你可以使用 json 辅助函数来自动将 Content-Type 标头设置为 application/json 并为你序列化 JSON 对象。
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return json({ message: 'Hello, World!' })
},
})
// Visit /api/hello to see the response
// {"message":"Hello, World!"}
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return json({ message: 'Hello, World!' })
},
})
// Visit /api/hello to see the response
// {"message":"Hello, World!"}
你可以通过以下任一方式设置响应的状态码
将其作为第二个参数的属性传递给 Response 构造函数
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ request, params }) => {
const user = await findUser(params.id)
if (!user) {
return new Response('User not found', {
status: 404,
})
}
return json(user)
},
})
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ request, params }) => {
const user = await findUser(params.id)
if (!user) {
return new Response('User not found', {
status: 404,
})
}
return json(user)
},
})
使用来自 @tanstack/react-start/server 的 setResponseStatus 辅助函数
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
import { setResponseStatus } from '@tanstack/react-start/server'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ request, params }) => {
const user = await findUser(params.id)
if (!user) {
setResponseStatus(404)
return new Response('User not found')
}
return json(user)
},
})
// routes/api/hello.ts
import { json } from '@tanstack/react-start'
import { createAPIFileRoute } from '@tanstack/react-start/api'
import { setResponseStatus } from '@tanstack/react-start/server'
export const APIRoute = createAPIFileRoute('/users/$id')({
GET: async ({ request, params }) => {
const user = await findUser(params.id)
if (!user) {
setResponseStatus(404)
return new Response('User not found')
}
return json(user)
},
})
在此示例中,如果未找到用户,我们将返回 404 状态码。你可以使用此方法设置任何有效的 HTTP 状态码。
有时你可能需要在响应中设置标头。你可以通过以下任一方式执行此操作
将对象作为第二个参数传递给 Response 构造函数。
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response('Hello, World!', {
headers: {
'Content-Type': 'text/plain',
},
})
},
})
// Visit /api/hello to see the response
// Hello, World!
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
return new Response('Hello, World!', {
headers: {
'Content-Type': 'text/plain',
},
})
},
})
// Visit /api/hello to see the response
// Hello, World!
或使用来自 @tanstack/react-start/server 的 setHeaders 辅助函数。
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
import { setHeaders } from '@tanstack/react-start/server'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
setHeaders({
'Content-Type': 'text/plain',
})
return new Response('Hello, World!')
},
})
// routes/api/hello.ts
import { createAPIFileRoute } from '@tanstack/react-start/api'
import { setHeaders } from '@tanstack/react-start/server'
export const APIRoute = createAPIFileRoute('/api/hello')({
GET: async ({ request }) => {
setHeaders({
'Content-Type': 'text/plain',
})
return new Response('Hello, World!')
},
})
你的每周 JavaScript 新闻。每周一免费发送给超过 10 万名开发者。