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

发布时间:2026/11/8 20:50:10

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/11/3 5:53:31

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/11/8 20:46:59

高压电工证测交流电压千万别踩坑,外省证怎么转过来全讲透

高压电工证测交流电压千万别踩坑,外省证怎么转过来全讲透 之前考的证在外省能不能转过来?这事儿压在我心头快一年了,现在终于找到答案了。作为一名河南应届工程类毕业生,我太清楚证书在手到底有多吃香了,尤其是高压电工证测交流电压这个技能,是很多单位…

2026/11/8 20:46:59

沭阳电工证培训免费怎么找?2026河南最新政策你得知道

沭阳电工证培训免费怎么找?2026河南最新政策你得知道 不知道去哪报名怕被中介坑?你不是一个人。特别是像你这样的初次报考人员,面对电工证培训,担心被套路、被坑钱,完全正常。2026年河南最新政策下,电工证考试报名门槛、培训内容、考试流程都有…

2026/11/8 20:46:59

广西电工证怎么样官方报名入口怎么找,实操考试怕挂科别慌

广西电工证怎么样官方报名入口怎么找,实操考试怕挂科别慌 实操考试心里没底怕挂科?你不是一个人。广西电工证怎么样,官方报名入口在哪?这些问题困扰着很多想考电工证的朋友,尤其是像我这样在鹤壁市政公用工程一线工作的从业者,对电工证的报考流程和实操…

2026/11/8 20:46:59

电工证科二好难怎么过?多少钱能搞定?

电工证科二好难怎么过?多少钱能搞定? 学历不高怕报不上名,电工证科二考试真的让人头疼。尤其是现在政策越来越严,很多人都在问:“ 电工证科二好难 , 多少钱 能过?”其实,问题并没有想象中那么复杂,关键在于你有没有找对方法和资源。…

2026/11/8 20:46:58

安丘低压电工证怎么考?2026河南最新政策下工地电工太忙还能考吗

安丘低压电工证怎么考?2026河南最新政策下工地电工太忙还能考吗 工地太忙根本没时间复习考试,这是很多一线电工的共同困扰。特别是像安丘低压电工证这类必须持证上岗的证书,不少工人在忙碌的工作节奏中,根本无暇顾及备考。但2026河南最新政策对特…

2026/11/8 0:00:27

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/8 0:00:27

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/7 14:54:47

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/7 14:50:12

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

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

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

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