Medusa 自定义 API 路由(Custom API Routes)实战指南:文件系统路由、路径参数与中间件体系

发布时间:2026/9/10 22:34:35

Medusa 自定义 API 路由(Custom API Routes)实战指南:文件系统路由、路径参数与中间件体系 Medusa 自定义 API 路由Custom API Routes实战指南文件系统路由、路径参数与中间件体系【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusaMedusa 的 API 路由API Route是一个 REST API 端点它采用文件即路由的约定式设计只要在应用的src/api目录下按约定命名与组织文件就能自动注册出完整的 HTTP 接口无需手动挂载 Express 路由。本文以 loyalty 插件 的真实实现为佐证系统讲解路由文件的创建方式、支持的全部 HTTP 方法、路径参数、依赖注入容器req.scope以及middlewares.ts中间件体系的完整配置方法并深入 framework 路由加载器 源码揭示文件系统路由在启动时是如何被扫描、解析与注册的。读完本文你可以独立在 Medusa 应用中编写出可用的自定义管理端、商城端乃至认证端 REST 接口。文件系统路由从route.ts文件到 REST 端点Medusa 应用中的 API 路由创建在/src/api目录下的 TypeScript 或 JavaScript 文件中文件名必须是route.ts或route.js。文件在目录中的相对位置决定了该端点对外暴露的 URL 路径文件中导出的函数名则决定了它响应的 HTTP 方法。例如要创建一个GET /store/hello-world端点只需创建文件src/api/store/hello-world/route.ts内容如下import type { MedusaRequest, MedusaResponse } from medusajs/framework/http; export async function GET(req: MedusaRequest, res: MedusaResponse) { res.json({ message: Hello world!, }); }启动应用后向/store/hello-world发起GET请求即可得到{ message: Hello world! }的 JSON 响应。从源码角度确认这一机制框架的RoutesLoader.scanDirroutes-loader.ts会递归扫描源目录只挑选文件名恰好为route、扩展名为.js或.ts的文件并把它们的相对路径转换为 URL 匹配模式随后注册进路由表。src/api目录中以下划线_开头的目录/文件段会被跳过routeFilePathSegment.some((segment) segment.startsWith(_))可用于放置不希望暴露为路由的辅助模块。支持的 HTTP 方法与多方法处理基于文件的约定式路由支持以下 HTTP 方法GETPOSTPUTPATCHDELETEOPTIONSHEAD你可以在同一个route.ts文件中通过导出与 HTTP 方法同名的函数为同一路径定义多个方法的处理器import type { MedusaRequest, MedusaResponse } from medusajs/framework/http; export async function GET(req: MedusaRequest, res: MedusaResponse) { // Handle GET requests } export async function POST(req: MedusaRequest, res: MedusaResponse) { // Handle POST requests } export async function PUT(req: MedusaRequest, res: MedusaResponse) { // Handle PUT requests }路由加载器在解析文件导出时只把名字属于HTTP_METHODS列表且类型为函数的导出当作路由处理器其余导出如配置标志会被忽略routes-loader.ts。方法集合定义在 types.ts 的HTTP_METHODS常量中与上述 7 个方法一一对应。路径参数用[param]目录捕获动态片段要创建接收路径参数path parameter的路由在路由路径中创建一个名字形如[param]的目录即可。例如定义一个接收productId参数的路由创建文件/api/products/[productId]/route.tsimport type { MedusaRequest, MedusaResponse, } from medusajs/framework/http export async function GET(req: MedusaRequest, res: MedusaResponse) { const { productId } req.params; res.json({ message: Youre looking for product ${productId} }) }路径参数会出现在req.params对象中可直接解构使用。若要接收多个路径参数在文件路径中创建多个[param]目录即可。例如同时接收productId与variantId创建文件/api/products/[productId]/variants/[variantId]/route.ts请求/products/prod_123/variants/var_456时req.params即为{ productId: prod_123, variantId: var_456 }。底层实现上RoutesLoader.createRoutePathroutes-loader.ts会通过正则PARAM_SEGMENT_MATCHER /\[(\w)\]/把[xxx]目录名转换为 Express 的:xxx参数语法并在同一路径中发现重复参数名时直接抛出错误Duplicate parameters found in route ...避免歧义。loyalty 插件中就有一个典型的双用途参数路由store/gift-cards/[idOrCode]/route.ts 用[idOrCode]目录同时承接礼品卡 ID 或兑换码export const GET async ( req: AuthenticatedMedusaRequestStoreGetGiftCardParams, res: MedusaResponse ) { const query req.scope.resolve(ContainerRegistrationKeys.QUERY); const { idOrCode: code } req.params; // ...通过 query.graph 按 code 查询礼品卡未命中时抛出 NOT_FOUND res.json({ gift_card }); };在路由中使用容器req.scopeMedusa 的依赖注入容器container在路由处理器中通过req.scope暴露。可以用它解析模块的主服务module service以及其他已注册的资源完成业务逻辑import type { MedusaRequest, MedusaResponse, } from medusajs/framework/http export const GET async ( req: MedusaRequest, res: MedusaResponse ) { const productModuleService req.scope.resolve(product) const [, count] await productModuleService.listAndCount() res.json({ count, }) }上面的示例解析了product模块的主服务并调用listAndCount()统计商品总数。在实践中loyalty 插件更常解析框架注册的通用查询服务注册键ContainerRegistrationKeys.QUERY来执行 GraphQL 风格的实体查询例如 store/store-credit-accounts/claim/route.ts 中的POST处理器它先解析QUERY服务与认证上下文req.auth_context.actor_id运行claimStoreCreditAccountWorkflow.run(...)工作流完成领券业务再通过graph.graph({ entity: store_credit_account, ... })查询结果并返回。除了服务你还可以在处理器中访问req.auth_context当前请求的认证信息actor_id等适用于经过认证的路由req.queryConfig由查询校验中间件解析出的字段选择配置req.body请求体内容。中间件体系middlewares.ts与defineMiddlewares可以为路由挂载中间件在/api/middlewares.ts中导出一个配置对象声明将哪些中间件应用到哪些路由上。例如要为/store/custom路由应用一个自定义中间件函数在/api/middlewares.ts中写入import { defineMiddlewares } from medusajs/framework/http import type { MedusaRequest, MedusaResponse, MedusaNextFunction, } from medusajs/framework/http; async function logger( req: MedusaRequest, res: MedusaResponse, next: MedusaNextFunction ) { console.log(Request received); next(); } export default defineMiddlewares({ routes: [ { matcher: /store/custom, middlewares: [logger], }, ], })其中matcher可以是字符串或正则表达式用于匹配要应用中间件的路由middlewares接收一个中间件函数数组。从源码看defineMiddlewares是medusajs/framework/http提供的辅助函数define-middlewares.ts它规范化配置结构并返回MiddlewaresConfig。真正的加载工作由MiddlewareFileLoadermiddleware-file-loader.ts完成——它专门扫描名为middlewaresMIDDLEWARE_FILE_NAME的文件读取其 default 导出配置。中间件路由配置的完整字段根据 types.ts 中MiddlewareRoute与MiddlewaresConfig的类型定义每条中间件路由配置支持以下字段字段类型说明matcherstring \| RegExp匹配要应用中间件的路由路径methodsMiddlewareVerb[]限定该配置仅对指定 HTTP 方法生效USE/ALL或 7 个标准方法旧版字段method已标记为废弃middlewares中间件函数数组依次应用到匹配路由的中间件bodyParserfalse \| { sizeLimit?, preserveRawBody? }覆盖该路由的 body parser 配置false表示关闭解析也可自定义大小限制additionalDataValidatorZodRawShape以 Zod shape 声明额外的请求数据校验规则与既有路由校验器合并policies{ resource, operation }[]声明该路由所需的 RBAC 资源与操作权限methods字段让同一matcher可以对 GET 与 POST 应用不同的中间件组合。loyalty 插件的 store/gift-cards/middlewares.ts 展示了这一实践export const storeGiftCardsMiddlewares: MiddlewareRoute[] [ { method: [GET], matcher: /store/gift-cards/:code, middlewares: [ validateAndTransformQuery( StoreGetGiftCardParams, retrieveGiftCardTransformQueryConfig ), ], }, ];这里通过method: [GET]限定方法用validateAndTransformQuery对查询参数进行 Zod 校验与字段转换——注意matcher中直接使用了 Express 风格的:code参数写法与文件系统路由中[idOrCode]目录生成的:idOrCode是同一套参数机制。统一组织多个路由的中间件插件或应用通常为每个子目录维护一份middlewares.ts如 admin/gift-cards/middlewares.ts、store/carts/middlewares.ts再在顶层 api/middlewares.ts 统一汇总import { defineMiddlewares } from medusajs/framework; import { adminGiftCardMiddlewares } from ./admin/gift-cards/middlewares; import { storeGiftCardsMiddlewares } from ./store/gift-cards/middlewares; // ... export default defineMiddlewares({ routes: [ ...adminGiftCardMiddlewares, ...storeGiftCardsMiddlewares, // ... ], });这种局部声明、顶层汇总的组织方式使每个业务子模块的中间件职责内聚、便于复用与测试。自定义全局错误处理defineMiddlewares的配置对象还支持顶层errorHandler字段值为false或自定义错误处理函数用于覆盖全局错误处理行为。未提供时框架使用内置的 error-handler 中间件 统一格式化异常响应。深入原理路由是如何被扫描与注册的框架对src/api目录的处理分为两个阶段均由 express-loader.ts 在应用启动时驱动路由加载RoutesLoader扫描route.ts文件生成RouteDescriptor并注册。每个描述符包含matcherURL 模式、methodHTTP 方法、handler处理函数以及一组标志位types.tsoptedOutOfAuth是否跳过认证shouldAppendAdminCors/shouldAppendStoreCors/shouldAppendAuthCors是否为对应前缀的路由附加 CORS 策略。中间件加载MiddlewareFileLoader扫描middlewares.ts解析出中间件描述符、body parser 配置路由、附加数据校验路由与全局错误处理函数再统一装配到 Express 应用上。其中路由类型admin / store / auth是根据路径前缀自动判定的RoutesLoader用ADMIN_ROUTE_MATCH /(\/admin$|\/admin\/)/、STORE_ROUTE_MATCH /(\/store$|\/store\/)/、AUTH_ROUTE_MATCH /(\/auth$|\/auth\/)/三个正则routes-loader.ts识别前缀并据此附加对应的 CORS 策略与认证行为。认证与 CORS 的控制标志默认情况下所有路由都会被框架的认证机制保护shouldAuthenticate默认为true并附加 CORS 策略。如果某个路由需要跳过认证例如公开的 Webhook 回调端点可以在route.ts中显式导出标志来覆盖默认行为export const AUTHENTICATE false; // 跳过该路由的认证 export const CORS false; // 跳过该路由的 CORS 策略路由加载器通过AUTHTHENTICATION_FLAG AUTHENTICATE与CORS_FLAG CORS检查文件导出routes-loader.ts并将结果写入描述符的optedOutOfAuth与shouldAppend*Cors标志。在 中间件夹具 与 routes-loader 相关测试 中你可以看到这些标志与中间件装配行为的完整验证用例。实践要点小结每个路由文件必须命名为route.ts/route.js位于应用src/api目录下目录层级即 URL 路径[name]目录即路径参数。一个文件可同时导出多个 HTTP 方法处理器非方法名的导出AUTHENTICATE、CORS标志用于控制认证与 CORS 行为。业务逻辑通过req.scope.resolve(...)获取模块服务与容器资源工作流可通过workflow.run({ input, container: req.scope })编排复杂业务。中间件集中在src/api/middlewares.ts中声明使用defineMiddlewares组织matchermethodsmiddlewares以及bodyParser、additionalDataValidator、policies的配置项loyalty 插件提供了多子模块中间件文件汇总合并的成熟范式。框架的路由与中间件加载实现在 routes-loader.ts、middleware-file-loader.ts 与 express-loader.ts 中配合tests下的测试用例可作为你理解与调试自定义路由行为的权威参考。【免费下载链接】medusaThe worlds most flexible commerce platform for agents and developers项目地址: https://gitcode.com/GitHub_Trending/me/medusa创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/10 22:34:35

cpp-httplib上手指南:单头文件搞定C++ HTTP服务

cpp-httplib上手指南:单头文件搞定C HTTP服务 【免费下载链接】cpp-httplib A C header-only HTTP/HTTPS server and client library 项目地址: https://gitcode.com/GitHub_Trending/cp/cpp-httplib 给C项目加一个HTTP接口时,常见的两难是&#…

2026/9/10 23:14:39

Elasticsearch核心原理与实战优化指南

1. Elasticsearch初探:为什么它成为搜索领域的标杆 第一次接触Elasticsearch时,我被它处理海量数据的速度震惊了。当时需要从2000万条日志中找出特定错误信息,传统数据库查询耗时近10分钟,而Elasticsearch仅用0.3秒就返回了结果。…

2026/9/10 16:39:38

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/10 11:16:38

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/9 16:31:09

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/10 0:00:55

目录对比去重实战:用哈希算法精准清理重复文件

我电脑里现在还有一块换了三次机的“数据墓地”硬盘,里面存着2016年以前所有旧笔记本的完整备份。平时不觉得有什么,直到前阵子想把它整理归档,发现同一个安装包、同一批照片、同一份论文草稿,在几个不同的备份目录里反复出现。更…

2026/9/10 0:00:55

Leaflet离线地图完整Demo合集:内网部署与坐标纠偏实战

简介:这是一份面向Web GIS开发者的LeafLet离线地图示例合集,帮助开发者快速掌握离线地图从搭建到交互的完整流程。压缩包共723个文件,大小14.06MB,以319个js脚本、175个html页面和29个css样式文件为主体,配合png/svg图…

2026/9/10 0:00:55

MATLAB读取Rinex 3.02观测文件:多系统GNSS数据解析实战

简介:基于MATLAB开发的Rinex3.02版观测文件(o文件)读取代码包,面向卫星定位导航方向的学习者与研究人员,用于解决新版观测文件的数据解析、历元提取与时间转换问题。压缩包共4个文件,包含两个m脚本、一个19…

2026/9/10 12:32:02

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/10 15:19:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/10 15:49:53

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码