Building your application

API 路由

编辑此页面

虽然服务端函数是为 UI 所需数据编写服务器端代码的好方法,但有时你需要暴露 API 路由。需要 API 路由的一些原因包括:

  • 有其他客户端想要共享这个逻辑。
  • 暴露 GraphQL 或 tRPC 端点。
  • 暴露面向公众的 REST API。
  • 为 OAuth 编写 webhook 或身份验证回调处理程序。
  • URL 不提供 HTML,而是提供其他类型的文档,如 PDF 或图片。

对于这些用例,SolidStart 提供了一种编写这些路由的方式,使其易于理解和维护。API 路由与其他路由类似,遵循与 UI 路由 相同的文件名约定。

API 路由和 UI 路由的区别在于从文件中导出的内容。UI 路由导出默认的 Solid 组件,而 API 路由则不然。相反,它们导出的函数以它们处理的 HTTP 方法命名。


编写 API 路由

要编写 API 路由,你可以在目录中创建一个文件。虽然你可以为这个目录取任何名字,但通常将其命名为 api 以表明该目录中的路由用于处理 API 请求:

routes/api/test.ts
export function GET() {
// ...
}
export function POST() {
// ...
}
export function PATCH() {
// ...
}
export function DELETE() {
// ...
}

API 路由会将 APIEvent 对象作为第一个参数传递。此对象包含:

  • request: Request 对象表示客户端发送的请求。
  • params: 包含动态路由参数的对象。例如,如果路由是 /api/users/:id,且请求发送到 /api/users/123 ,那么 params 将是 { id: 123 }
  • fetch: 一个内部 fetch 函数,可用于向其他 API 路由发出请求,而无需担心 URL 的 origin

API 路由预期返回 JSON 或一个 Response 对象。为了处理所有方法,你可以定义一个绑定多个方法的处理函数:

routes/api/all.ts
async function handler() {
// ...
}
export const GET = handler;
export const POST = handler;
// ...

下面展示了一个返回特定类别和品牌产品的 API 路由示例:

routes/api/product/[category]/[brand].ts
import type { APIEvent } from "@solidjs/start/server";
import store from "./store";
export async function GET({ params }: APIEvent) {
console.log(`Category: ${params.category}, Brand: ${params.brand}`);
const products = await store.getProducts(params.category, params.brand);
return products;
}

会话管理

由于 HTTP 是无状态协议,你需要在服务器上管理会话状态。例如,如果你想知道用户是谁,最安全的方式是通过 HTTP-only cookies。Cookie 是一种在用户浏览器中存储数据的方式,这些数据在请求之间保持持久化。

用户的请求通过 Request 对象暴露。通过解析 Cookie 头部,可以访问 cookie,并且可以使用 vinxi/http 中的辅助函数来使这个过程更简单。

import type { APIEvent } from "@solidjs/start/server";
import { getCookie } from "vinxi/http";
import store from "./store";
export async function GET(event: APIEvent) {
const userId = getCookie("userId");
if (!userId) {
return new Response("Not logged in", { status: 401 });
}
const user = await store.getUser(event.params.userId);
if (user.id !== userId) {
return new Response("Not authorized", { status: 403 });
}
return user;
}

在这个示例中,你可以看到从cookie中读取的 userId 被用来在存储中查找用户。有关如何使用cookie进行安全会话管理的更多信息,请阅读session 文档


暴露 GraphQL API

SolidStart 使实现 GraphQL API 变得容易。GraphQL 是一种 API 查询语言,也是一个通过使用你为数据定义的类型系统来执行这些查询的运行时。

要实现 GraphQL API,你需要定义 schema 和解析器。graphql 函数接收一个GraphQL schema 并返回一个可用作 API 路由处理程序的函数。

首先,要实现 GraphQL API,需要安装 graphql 库。之后,你可以在文件中实现你的 schema 和解析器,然后导出一个处理函数,该函数将用作API路由:

routes/graphql.ts
import { buildSchema, graphql } from "graphql";
import type { APIEvent } from "@solidjs/start/server";
// Define GraphQL Schema
const schema = buildSchema(`
type Message {
message: String
}
type Query {
hello(input: String): Message
goodbye: String
}
`);
// Define GraphQL Resolvers
const rootValue = {
hello: () => {
return {
message: "Hello World"
};
},
goodbye: () => {
return "Goodbye";
}
};
// request handler
const handler = async (event: APIEvent) => {
// get request body
const body = await new Response(event.request.body).json();
// pass query and save results
const result = await graphql({ rootValue, schema, source: body.query });
// send query result
return result;
};
export const GET = handler;
export const POST = handler;

暴露 tRPC 服务端路由

tRPC 是一个现代的 TypeScript 优先的 API 框架,设计主旨在于易于使用和理解。

要暴露 tRPC 服务端路由,你需要编写你的路由器。一旦编写完路由器,你可以将它放在一个单独的文件中,这样你就可以为你的客户端导出类型。

lib/router.ts
import { initTRPC } from "@trpc/server";
import { wrap } from "@decs/typeschema";
import { string } from "valibot";
const t = initTRPC.create();
export const appRouter = t.router({
hello: t.procedure.input(wrap(string())).query(({ input }) => {
return `hello ${input ?? "world"}`;
})
});
export type AppRouter = typeof appRouter;

下面展示了一个可用于从 tRPC 服务端获取数据的简单客户端示例:

lib/trpc.ts
import { createTRPCProxyClient, httpBatchLink, loggerLink } from "@trpc/client";
import type { AppRouter } from "./router";
export const client = createTRPCProxyClient<AppRouter>({
links: [loggerLink(), httpBatchLink({ url: "http://localhost:3000/api/trpc" })]
});

最后,您可以使用 fetch 适配器来编写一个作为 tRPC 服务端 的 API 路由。

routes/api/trpc/[trpc].ts
import { type APIEvent } from "solid-start/api";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { appRouter } from "~/lib/router";
const handler = (event: APIEvent) =>
fetchRequestHandler({
endpoint: "/api/trpc",
req: event.request,
router: appRouter,
createContext: () => ({})
});
export const GET = handler;
export const POST = handler;

要了解更多关于tRPC的信息,您可以阅读 tRPC 文档

报告此页面问题