资讯详情

资讯详情

Wasp 自定义 HTTP API 端点(api 声明)完整实战指南:路由、认证、中间件与实体注入

Wasp 自定义 HTTP API 端点api 声明完整实战指南路由、认证、中间件与实体注入【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp本篇指南围绕 Wasp当前仓库为GitHub_Trending/wa/wasp中通过api声明创建自定义 HTTP API 端点的完整流程展开。你将掌握在.wasp文件中声明 API、用 Express 风格的 NodeJS 函数实现它、从客户端或外部调用它、通过apiNamespace与middlewareConfigFn精确控制 CORS 与中间件、在context中注入实体与用户会话信息的全部细节。读完即可在 Wasp 项目中写出可复用的自定义 REST 端点包括流式响应场景。Wasp 默认的客户端—服务端交互机制是 Operationsquery/action详见 Operations 概览。但当你需要特定的 URL 方法/路径组合、特定的响应格式或需要完全掌控一个端点的行为时Operations 就不再合适。此时你应当使用api声明——它把一段 JS/TS 函数绑定到形如POST /something/special的 HTTP 端点上。与 Operations 不同api没有任何客户端辅助函数如useQuery但它仍然可以像普通 Express 路由一样被浏览器、curl、Postman 或任意 Web 服务直接调用也能通过 Wasp 提供的 HTTP 客户端从你自己的前端调用。如何创建一个 API创建一个 Wasp API 只需要两步在 Wasp 文件中用api声明描述这个端点编写它的 NodeJS 实现函数。完成这两步后你就可以从客户端代码通过 Wasp 的 HTTP 客户端包装器或从外部世界调用这个 API 了。在 Wasp 文件中声明 API在main.wasp中使用api声明即可定义端点。API 声明与它的实现不需要同名当然同名也可以下面是一个最简单的示例// ... api fooBar { // API 与其实现不必但可以同名。 fn: import { fooBar } from server/apis.js, httpRoute: (GET, /foo/bar) }fn指向实现函数的 import 语句httpRoute是一个(HttpMethod, string)元组string是 Express 风格的路由路径。关于各字段的完整说明见后文 API Reference。定义 API 的 NodeJS 实现:::note 对 TypeScript 用户为了确保 Wasp 编译器为 API 生成可供实现使用的类型请先把api声明写进.wasp文件并保持wasp start运行。Wasp 会根据声明自动生成wasp/apis/types中的类型在 0.11.8 版本中实现文件中通过import { FooBar } from wasp/apis/types引入。 :::实现函数接收三个参数reqExpress Request 对象resExpress Response 对象context由 Wasp 注入的附加上下文对象包含用户会话信息以及实体信息。为简洁起见下面例子暂不使用context其详细用法见 在 API 中使用实体。import { FooBar } from wasp/apis/types; // 该类型由 Wasp 基于上面的 api 声明自动生成。 export const fooBar: FooBar (req, res, context) { res.set(Access-Control-Allow-Origin, *); // 示例修改响应头以覆盖 Wasp 默认 CORS 中间件。 res.json({ msg: Hello, ${context.user?.username || stranger}! }); };JavaScript 版本同样简单无需类型导入export const fooBar (req, res, context) { res.set(Access-Control-Allow-Origin, *); res.json({ msg: Hello, ${context.user?.username || stranger}! }); };这个实现就是一个标准 Express 请求处理器你可以像在任意 Express 应用中一样读取req、设置响应头、返回 JSON。为 API 提供额外类型信息TypeScript假设你想创建一个GET路由它从 URL 参数中接收一个 email 地址并返回生命、宇宙以及一切的答案——在 TypeScript 中长这样先在 Wasp 中声明 APIapi fooBar { fn: import { fooBar } from server/apis.js, entities: [Task], httpRoute: (GET, /foo/bar/:email) }然后在实现中使用FooBar泛型传入params与response两个类型参数即可获得完整的类型安全import { FooBar } from wasp/apis/types; export const fooBar: FooBar { email: string }, // params { answer: number } // response (req, res, _context) { console.log(req.params.email); res.json({ answer: 42 }); };此时req.params.email的类型会被推导为string而res.json(...)的入参类型也会被约束为{ answer: number }。这一机制源于 Wasp 生成的 SDK 类型在仓库中查看 SDK 的 API 类型模板可以看到 Wasp 会为每个api声明生成一个带P extends ExpressParams ExpressParams、ResBody any、ReqBody any等泛型参数的别名类型0.11.8 模板位于 Apis 类型生成模板这正是泛型FooBarParams, ResBody的底层来源。使用 API从外部使用 API从外部调用非常简单直接使用你声明的 HTTP 方法与路径发起请求即可。例如你的应用运行在https://example.com那么上面的声明对应GET https://example.com/foo/bar文档原文示例为/foo/callback请以你声明的路径为准可以在浏览器、Postman、curl或任意 Web 服务中调用。从客户端使用 API从客户端调用自定义 API包括携带认证信息时可以导入wasp/api提供的 Axios 包装器import React, { useEffect } from react; import api from wasp/api; async function fetchCustomRoute() { const res await api.get(/foo/bar); console.log(res.data); } export const Foo () { useEffect(() { fetchCustomRoute(); }, []); return // .../; };TypeScript 版本完全一致import React, { useEffect } from react; import api from wasp/api; async function fetchCustomRoute() { const res await api.get(/foo/bar); console.log(res.data); } export const Foo () { useEffect(() { fetchCustomRoute(); }, []); return // .../; };仓库中的 kitchen-sink 示例提供了一个真实的落地样例ApisPage.tsx 通过api.get(endpoint).json()分别请求需要认证的/foo/bar与无需认证的/bar/baz并用useQuery包装以展示 loading / error / data 三种状态。配套的 e2e 测试 验证了未登录时认证 API 返回错误、/bar/baz正常返回Hello, stranger!登录后认证 API 返回Hello, email!的完整行为可以直接作为你端到端验证自定义 API 的参考。确保 CORS 正常工作API 被设计为尽可能灵活因此它们不像 Operations 那样默认挂载中间件。要在客户端正常使用这些 API你必须确保 CORS跨域资源共享被启用。做法是在 Wasp 文件中为 API 定义自定义中间件。例如apiNamespace就是一种简单声明用来把某个middlewareConfigFn应用到某个路径下的所有 APIapiNamespace fooBar { middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from server/apis.js, path: /foo }然后在实现文件中返回默认配置TS 版本引入MiddlewareConfigFn类型import { MiddlewareConfigFn } from wasp/middleware; export const apiMiddleware: MiddlewareConfigFn (config) { return config; };返回默认中间件配置即表示/foo路径下的所有 API 都启用 CORS。更完整的中间件定制说明见 中间件配置。从源码层面看apiNamespace在 ApiNamespace.hs 中被定义为仅含middlewareConfigFn :: ExtImport与path :: String两个字段的数据结构。生成阶段会把它编译为router.use(path, globalMiddlewareConfigForExpress(...))见 生成模板即挂在路由层级的路径级中间件。在 API 中使用实体多数情况下API 中要操作的资源都是 实体Entity。要把实体注入 API只需在api声明的entities字段中列出它们api fooBar { fn: import { fooBar } from server/apis.js, entities: [Task], httpRoute: (GET, /foo/bar) }Wasp 会把列出的实体注入 API 的context参数从而让你直接访问该实体的 Prisma APIimport { FooBar } from wasp/apis/types; export const fooBar: FooBar (req, res, context) { res.json({ count: await context.entities.Task.count() }); };context.entities.Task暴露的就是 Prisma CRUD API 中的prisma.task。从生成代码看这一注入由 ApiRoutesG.hs 中的getApiEntitiesObject完成最终在 生成模板 中表现为构造context.entities { Task: prisma.task, ... }传给实现函数。kitchen-sink 示例中apis.wasp.ts 的/foo/bar与/bar/baz两个 API 都声明了entities: [Task]。API 中auth字段与context.userapi声明中的auth: bool字段控制该端点是否解析 JWT当项目启用了认证时auth默认为true实现函数的context中会提供context.user对象如果你不希望该端点尝试解析 Authorization Header 中的 JWT例如公开的 webhook 回调请显式设置为false。从实现看ApiRoutesG.hs 中的isAuthEnabledForApi spec api fromMaybe (isAuthEnabled spec) (Api.auth api)表明API 的auth取值优先于全局认证开关——未显式声明时回退到项目全局是否启用认证。生成模板中启用认证的路由会被编译为router.method(path, [auth, ...middleware], defineHandler(...))并把makeAuthUserIfPossible(req.user)的结果放进context.user见 生成模板。API Referenceapi声明的完整字段如下完整示例见 apis.wasp.tsapi fooBar { fn: import { fooBar } from server/apis.js, httpRoute: (GET, /foo/bar), entities: [Task], auth: true, middlewareConfigFn: import { apiMiddleware } from server/apis.js }fn: ServerImport必填该 API NodeJS 实现的 import 语句。httpRoute: (HttpMethod, string)必填HTTP 方法与路径的二元组。方法可以是ALL、GET、POST、PUT、DELETE路径是 Express 路径字符串支持:param、通配符等 Express 语法。在 Api.hs 中该字段被定义为(HttpMethod, String)HttpMethod数据构造器恰好为ALL | GET | POST | PUT | DELETE且编译器会在 Valid.hs 的validateApiRoutesAreUnique中校验所有 API 的方法、路径组合唯一——同一路径上声明相同方法或声明ALL会与其他方法构成冲突并报错apiroutes must be unique。entities: [Entity]希望在 API 内部使用的实体列表会注入context.entities详见 在 API 中使用实体。auth: bool启用认证时默认true并提供context.user对象。如果不想解析 Authorization Header 中的 JWT设置为false。middlewareConfigFn: ServerImport该 API 的 Express 中间件配置函数 import 语句。未指定时使用默认中间件在生成模板中以idFn兜底见 生成模板指定后可以middlewareConfig.set/delete增删中间件。更多说明见 中间件配置。进阶用中间件定制一个非默认的 API由于api不使用 Operations 的默认中间件链你可以针对单个 API 完全替换其中的中间件这在处理 webhook 等场景时尤其有用。例如下面这个 webhook 回调将express.json替换为接收任意原始内容的express.rawapi webhookCallback { fn: import { webhookCallback } from server/apis.js, middlewareConfigFn: import { webhookCallbackMiddlewareFn } from server/apis.js, httpRoute: (POST, /webhook/callback), auth: false }import express from express import { WebhookCallback } from wasp/apis/types import type { MiddlewareConfigFn } from wasp/middleware export const webhookCallback: WebhookCallback (req, res, _context) { res.json({ msg: req.body.length }) } export const webhookCallbackMiddlewareFn: MiddlewareConfigFn (middlewareConfig) { middlewareConfig.delete(express.json) middlewareConfig.set(express.raw, express.raw({ type: */* })) return middlewareConfig }kitchen-sink 示例的 apis.ts 完整复现了这个模式fooBarMiddlewareFn用set(custom.route, ...)追加自定义中间件、webhookCallbackMiddlewareFn用delete/set替换express.json并且该 API 挂载在单条路由上而barNamespaceMiddlewareFn则展示了如何通过apiNamespace为/bar下所有 API 统一注入中间件。其默认中间件集合helmet、cors、morgan、express.json、express.urlencoded、cookieParser及各层级的定制方式详见 中间件配置。流式响应Streaming场景自定义 API 的另一个典型用途是流式响应利用 Express 的res.write()/res.end()把数据分块推送给客户端。在生成模板中实现函数被defineHandler包裹后直接作为路由处理器挂载见 生成模板因此原生 Express 的流式写法天然可用。kitchen-sink 示例提供了完整可运行样例export const streamingText: StreamingText async (_req, res, _context) { res.setHeader(Content-Type, text/html; charsetutf-8); res.setHeader(Transfer-Encoding, chunked); res.setHeader(Cache-Control, no-transform); // 防止代理如边缘 CDN压缩缓冲流 res.write(Hm, let me see...\n); // ...循环 res.write() 分块发送 res.end(); };对应地在 Wasp 文件中声明并确保为该路径启用 CORS 中间件api(GET, /api/streaming-test, streamingText), apiNamespace(/api/streaming-test, { middlewareConfigFn: defaultMiddlewareForStreamingText, }),客户端通过fetch的response.bodyReadableStream 逐块读取内容见 StreamingTestPage.tsx即可实现边生成边展示的效果——这正是 AI 场景下流式输出 LLM 回复的典型实现路径。小结Wasp 的api声明在保留 Operations 便捷性的同时把端点的完全控制权交还给了开发者两步即可上线一个自定义 REST 端点.wasp中声明 NodeJS 实现通过context统一获得用户会话context.user与实体 Prisma APIcontext.entities用middlewareConfigFn/apiNamespace精确控制 CORS 与中间件应对 webhook、原始 body、流式响应等特殊需求编译器自动校验路由唯一性并生成类型安全的 SDK 类型全程享受 TS 类型保障。相关参考Operations 概览、实体、中间件配置、示例实现 apis.wasp.ts 与 apis.ts。【免费下载链接】waspThe batteries-included full-stack framework for the AI era. Develop JS/TS web apps (React, Node.js, and Prisma) using declarative code that abstracts away complex full-stack features like auth, background jobs, RPC, email sending, end-to-end type safety, single-command deployment, and more.项目地址: https://gitcode.com/GitHub_Trending/wa/wasp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →