
后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载导读react-hybrid-api是 Midway 官方仓库中的一个可运行示例React Vite 技术栈它演示了如何在同一个 Midway 应用中混合使用两种路由风格通过defineApi声明的函数式路由以及通过Controller/Get装饰器声明的类路由。本文以 samples/react-hybrid-api/README.md 为核心骨架结合仓库源码讲解项目目录结构、启动与构建命令、两套路由的写法差异以及前端如何通过midwayjs/web-bridge的createClient统一调用这两类接口。读完本文你将掌握一套前后端同一仓库、一套 API 契约双端共用、混合路由共存的完整实战方案。一、示例概览为什么要做混合路由在很多迁移场景中团队既有用Controller装饰器写的老接口又想尝试 Midway 新一代的defineApi函数式路由。此时最大的痛点是两套写法是否能在同一个应用里共存前端调用是否要维护两套不同的客户端代码这个示例给出的答案是可以共存而且前端可以统一调用。示例中同时包含三类能力函数式路由src/server/api/user.api.ts中用defineApi定义/users下的getUser接口类路由src/server/controller/controller-route.controller.ts中用Controller/Get定义/controller-route/hello接口统一前端客户端src/web/api/client.ts中通过createClient生成api对象既能以api.user.getUser(...)的类型化方式调用函数式路由也能以api.call(controllerRouteHello, {})的方式按路由名调用类路由接口。后端两个接口都挂在globalPrefix: /api之下因此完整请求路径分别为/api/users/:id和/api/controller-route/hello详见后文测试部分。二、目录结构按照 samples/react-hybrid-api/README.md 的说明示例目录组织如下samples/react-hybrid-api/ package.json tsconfig.json tsconfig.server.json vite.config.ts src/ server/ configuration.ts bootstrap.ts api/ user.api.ts controller/ controller-route.controller.ts web/ app.tsx api/ client.ts整个示例采用了典型的前后端同仓布局src/server/Midway 服务端代码包含应用配置configuration.ts、启动入口bootstrap.ts、函数式路由api/user.api.ts与类路由controller/controller-route.controller.tssrc/web/React 前端代码api/client.ts是前后端共用的 API 客户端app.tsx是页面组件vite.config.tsVite 构建配置同时接入 Midway 的devPlugin开发期启动服务端与apiPlugin把defineApi源码转换为前端可用的契约模块。值得注意的细节是package.json中scripts声明了build由build:servertsc -p tsconfig.server.json与build:webvite build两步组成分别编译服务端与前端test:hmr:e2e指向仓库根目录的 scripts/samples-hmr-e2e.mjs说明该示例还纳入了 Midway 的 HMR 端到端回归体系。三、运行与构建原 README 给出了两条核心命令这里一并说明其背后做的事# 开发模式一键启动 Vite Midway 服务端含 HMR pnpm -C samples/react-hybrid-api dev # 生产构建先 tsc 编译服务端再 vite build 打包前端 pnpm -C samples/react-hybrid-api buildpnpm -C dir表示在指定子包目录下执行脚本对应 package.json 中的dev: vitedev依赖 Vite 配置中的devPlugin来自midwayjs/mock/vite它会在开发服务器内拉起 Midway 应用并配置appDir、baseDir与basePath详见第四节build分两步执行服务端用tsc -p tsconfig.server.json编译到独立输出前端用vite build输出到dist/webvite.config.ts中build.outDir指定仓库还提供了冒烟测试入口test:hmr:e2e可执行node ../../scripts/samples-hmr-e2e.mjs --sample react-hybrid-api验证示例的 HMR 链路。四、函数式路由defineApi 声明 API 契约函数式路由的核心实现位于 src/server/api/user.api.tsimport { defineApi } from midwayjs/core/functional; export const userApi defineApi(/users, api ({ getUser: api .get(/:id) .meta({ routerName: getUser }) .handle(async ({ input }) { return { id: input.params?.[id], name: harry, }; }), }));逐段拆解这段契约即实现的写法defineApi(/users, api ({ ... }))第一参数是路由前缀/users第二参数是工厂函数返回一个路由名 → 路由定义的映射对象。映射的键getUser既是前端调用时的方法名也是路由的语义标识api.get(/:id)声明 HTTP 方法GET与路径参数/:id。从 web-bridge 的解析器 packages/web-bridge/src/vite.ts 可以看到defineApi支持的方法包括get/post/put/delete/patch/options/head/all说明该写法可覆盖全部常见 HTTP 方法.meta({ routerName: getUser })为路由挂载元信息其中routerName是关键字段——它是路由的注册名会进入 Midway 的路由清单使得该接口也能被api.call(getUser)按名调用。meta还支持ignoreGlobalPrefix是否绕过全局前缀等选项vite.ts 的解析逻辑中会原样读出这些选项并注入生成的虚拟模块.handle(async ({ input }) { ... })真正的处理器。入参input携带解析后的请求数据示例中通过input.params?.[id]拿到路径参数。这种写法把路由声明与处理逻辑写在同一处无需单独的 Controller 类与装饰器因此被称作函数式路由。从源码结构看defineApi返回的既是一份路由契约可被前端复用为类型安全的调用签名又会在服务端被转换为实际的 Koa 路由这正是它能够实现前后端一份代码、双端共用的基础。五、类路由Controller/Get 装饰器风格示例同时保留了传统的装饰器路由实现在 src/server/controller/controller-route.controller.tsimport { Controller, Get } from midwayjs/core; Controller(/controller-route) export class ControllerRouteController { Get(/hello, { routerName: controllerRouteHello, }) hello() { return { message: hello from controller route, }; } }要点说明Controller(/controller-route)声明类级前缀Get(/hello)声明方法级路径与 HTTP 方法组合后的完整路径为/controller-route/helloGet的第二参数同样支持routerName选项这里命名为controllerRouteHello。这意味着装饰器路由同样可以被注册进路由清单从而被前端的api.call统一调用——这是两套路由风格能共存的连接点对比可见函数式路由把路由名通过.meta({ routerName })声明装饰器路由通过Get(path, { routerName })声明二者最终都汇入同一套路由名 → 路由的清单机制只是声明位置不同。六、统一前端客户端createClient 与 api.call前端调用的统一入口在 src/web/api/client.tsimport { createClient } from midwayjs/web-bridge; import { userApi } from ../../server/api/user.api.js; export const apiBridgeConfig { browserBasePath: /api, serverBasePath: http://127.0.0.1:7001/api, apiDir: src/server/api, } as const; export const api createClient( { user: userApi, }, { basePath: { browser: apiBridgeConfig.browserBasePath, server: apiBridgeConfig.serverBasePath, }, } );这段代码有两个关键点直接 import 服务端的user.api.js前端通过 Vite 的apiPlugin把defineApi源文件转换成仅含路由契约方法、路径、routerName 等的虚拟模块因此可以安全地在浏览器侧引用服务端定义的 API并获得完整的类型提示createClient的双 basePathbrowser路径用于浏览器端请求走同源相对路径/apiserver路径用于 SSR/服务端渲染场景显式指向http://127.0.0.1:7001/api。两个 basePath 与后端配置中的globalPrefix: /api与port: 7001一一对应配置一致性是请求能正确命中的前提。页面组件 src/web/app.tsx 演示了两种调用方式// 方式一类型化方法调用函数式路由 api.user .getUser({ params: { id: u-1 } }) .then(data setUser(data as User)) .catch(err setError(err?.message || String(err))); // 方式二按 routerName 调用类路由经路由清单运行时解析 api .call{ message?: string }(controllerRouteHello, {}) .then(data setControllerRouteMessage(data?.message || No message));api.user.getUser(...)对应服务端defineApi(/users, ...)中的getUser路由参数对象中的params会映射到路径/:idapi.call(controllerRouteHello, {})不依赖前端 import 的服务端模块而是运行时向服务端发起一次按路由名寻址的请求服务端从路由清单中找到对应路由并执行因此它既能调用函数式路由routerName: getUser同理可被调用也能调用装饰器路由routerName: controllerRouteHello。页面中特意用import.meta.env.DEV对装饰器路由调用做了限制生产构建下按钮禁用并提示 Only available in dev这是因为示例的类路由仅作为演示注入生产场景建议把装饰器路由也纳入统一的 API 契约管理。七、服务端配置globalPrefix、端口与 ESM 检测服务端配置位于 src/server/configuration.tsimport { ESModuleFileDetector } from midwayjs/core; import { defineConfiguration } from midwayjs/core/functional; import * as koa from midwayjs/koa; export default defineConfiguration({ imports: [koa], importConfigs: [ { default: { keys: midway-react-hybrid-api-sample-key, koa: { globalPrefix: /api, port: 7001, }, }, local: { koa: { port: null, }, }, }, ], detector: new ESModuleFileDetector(), });几个配置项的作用imports: [koa]引入midwayjs/koa组件示例运行在 Koa 之上koa.globalPrefix: /api为所有 HTTP 路由追加/api前缀与服务端 basePath、前端browserBasePath保持一致koa.port: 7001服务端监听端口Vite 开发模式下由devPlugin接管local环境端口设为null避免与开发服务器端口冲突detector: new ESModuleFileDetector()Midway 用于自动发现并加载 ES Module 文件如本示例中src/server/api与src/server/controller下的 TS 文件的检测器。服务端启动入口 src/server/bootstrap.ts 使用Bootstrap.configure({ baseDir: appDir }).run()启动应用并通过对process.argv[1]与import.meta.url的比对判断是否被直接运行从而保证同一文件既能作为模块被 Vite 开发服务器加载也能被tsc编译后直接node启动。八、Vite 集成devPlugin 与 apiPlugin 的分工src/web/../vite.config.ts 中注册了两个 Midway 相关插件import { apiPlugin } from midwayjs/web-bridge/vite; import { devPlugin } from midwayjs/mock/vite; import { apiBridgeConfig } from ./src/web/api/client; plugins: [ devPlugin({ appDir: fileURLToPath(new URL(., import.meta.url)), baseDir: fileURLToPath(new URL(./src/server, import.meta.url)), basePath: apiBridgeConfig.browserBasePath, }), react(), apiPlugin({ root: fileURLToPath(new URL(., import.meta.url)), apiDir: apiBridgeConfig.apiDir, // src/server/api target: both, }), ],devPlugin来自midwayjs/mock/vite在 Vite 开发服务器内拉起 Midway 应用baseDir指向src/serverbasePath与浏览器请求前缀/api一致从而实现一条pnpm dev同时跑前后端的开发体验apiPlugin来自midwayjs/web-bridge/vite负责把apiDirsrc/server/api下的defineApi源文件转换为前端可引用的契约模块target: both表示同时启用浏览器端client与 SSR 端转换对应其实现中的enableClientTransform/enableSsrTransform分支见 packages/web-bridge/src/vite.ts。从源码看apiPlugin的核心实现机制是文本级契约提取 虚拟模块它在resolveId阶段拦截对apiDir内文件的引用用内置的轻量解析器处理字符串、注释、括号配对后从defineApi工厂函数中提取出每个路由的method、path与meta.routerName等字段transformDefineApiSource再在load阶段把契约代码注入以\0midway-api:开头的虚拟模块toCode最后通过handleHotUpdate监听 api 文件变更以支持 HMR。这意味着前端能拿到类型安全的方法链靠的并不是运行时反射而是构建期对defineApi源码的静态契约提取这也解释了为何client.ts可以直接import ../../server/api/user.api.js而不会把服务端逻辑打进浏览器包。九、测试验证冒烟用例印证两条路由仓库为示例提供了冒烟测试 test/smoke.test.mjs使用midwayjs/mock的createApp/createHttpRequest直接对应用发起 HTTP 请求// 函数式路由GET /api/users/1 const result await createHttpRequest(app).get(/api/users/1); assert.equal(result.status, 200); assert.equal(result.body.id, 1); assert.equal(typeof result.body.name, string); // 类路由GET /api/controller-route/hello const result await createHttpRequest(app).get(/api/controller-route/hello); assert.equal(result.status, 200); assert.equal(result.body.message, hello from controller route);两个断言分别验证了函数式路由可访问GET /api/users/1返回{ id: 1, name: harry }路径参数:id正确解析类路由可访问GET /api/controller-route/hello返回{ message: hello from controller route }与控制器实现一致。测试中的完整路径/api/users/1、/api/controller-route/hello恰好印证了路由前缀 globalPrefix的叠加关系也说明两套路由在同一应用、同一前缀下并行注册、互不干扰。若在本地验证可执行pnpm -C samples/react-hybrid-api test对应cross-env NODE_ENVunittest mocha。十、小结混合路由方案的适用场景与迁移路径综合整个示例可以提炼出这套混合路由方案的几条实践要点新旧共存老接口保持Controller装饰器写法不动新接口用defineApi函数式写法二者在同一应用内并行注册configuration.ts 的imports: [koa]下同时加载两类路由统一调用前端统一走createClient生成的api对象——函数式路由享受api.user.getUser(params)的类型化调用装饰器路由通过api.call(controllerRouteHello, {})按路由名调用无需为两套路由维护不同的请求工具契约复用defineApi源文件同时承担服务端路由实现与前端调用契约两个角色靠apiPlugin的构建期契约提取实现新增接口时只需在src/server/api下增补一个文件配置一致性globalPrefix、端口、浏览器/服务端 basePath、apiDir四者必须对齐本例统一为/api、7001、src/server/api这是混合路由方案不出错的前提。如果你正在将存量 Midway 项目渐进迁移到函数式路由或希望在 React 前端中统一管理跨 Controller 的接口调用react-hybrid-api是一个可以直接复制运行的参考样板与之配套的还有 samples/react-functional-api纯函数式路由与 samples/functional-api-hybrid无前端、纯函数式与类路由混合的服务端样例可对照阅读以理解不同组合下的工程形态。赞分享后端微服务云原生【免费下载链接】midway A Node.js Serverless Framework for front-end/full-stack developers. Build the application for next decade. Works on AWS, Alibaba Cloud, Tencent Cloud and traditional VM/Container. Super easy integrate with React and Vue. 项目地址https://gitcode.com/gh_mirrors/mi/midway点击查看免费下载相关推荐Midway 混合路由实践类装饰器与 defineApi 函数式路由在同一应用中共存Midway 混合路由实践类装饰器与 defineApi 函数式路由在同一应用中共存 导读 本篇基于仓库中 samples/functional api hy后端微服务云原生Midway 函数式 CRUD 路由工厂用 defineCrudRoutes() 在 defineApi() 中生成标准增删改查接口Midway 函数式 CRUD 路由工厂用 defineCrudRoutes 在 defineApi 中生成标准增删改查接口 本文是一份面向 Midway 函后端微服务云原生React Starter Kit 前端路由实战TanStack Router 文件路由、鉴权守卫与边缘路由协同React Starter Kit 前端路由实战TanStack Router 文件路由、鉴权守卫与边缘路由协同 导读 本文基于 React Starter后端前端上一篇为什么你的单机游戏不能和朋友一起玩Nucleus Co-Op给出了完美答案下一篇terraform-provider-aws 静态检查器 AWSR001 解析杜绝硬编码 .amazonaws.com 域名后缀兼容多分区创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考