Skip to content

Commit a88883a

Browse files
fix: resource route middleware and add match() method
- fix(router): resource routes now properly apply middleware via group wrapping instead of no-op comment - feat(router): add match() method for multi-HTTP-method route registration (e.g., route.match(['GET', 'POST'], '/path', handler)) - fix(router): separate try/catch for user routes, ORM routes, and package discovery so failures in one don't block others - chore(router): bump @stacksjs/bun-router dependency to ^0.0.4 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a2f47ae commit a88883a

2 files changed

Lines changed: 59 additions & 26 deletions

File tree

storage/framework/core/router/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"prepublishOnly": "bun run build"
5050
},
5151
"dependencies": {
52-
"@stacksjs/bun-router": "^0.0.2",
52+
"@stacksjs/bun-router": "^0.0.4",
5353
"ts-rate-limiter": "^0.4.0"
5454
},
5555
"devDependencies": {

storage/framework/core/router/src/stacks-router.ts

Lines changed: 58 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -987,43 +987,65 @@ export function createStacksRouter(config: StacksRouterConfig = {}): StacksRoute
987987
resource(name: string, handler: string, options?: ResourceRouteOptions) {
988988
const actions: ResourceAction[] = ['index', 'store', 'show', 'update', 'destroy']
989989

990-
// Apply only/except filters
991-
let activeActions = options?.only
990+
const activeActions = options?.only
992991
? actions.filter(a => options.only!.includes(a))
993992
: options?.except
994993
? actions.filter(a => !options.except!.includes(a))
995994
: actions
996995

997996
const handlerBase = handler.replace(/Action$/, '')
998997

999-
for (const action of activeActions) {
1000-
switch (action) {
1001-
case 'index':
1002-
stacksRouter.get(`/${name}`, `${handlerBase}IndexAction`)
1003-
break
1004-
case 'store':
1005-
stacksRouter.post(`/${name}`, `${handlerBase}StoreAction`)
1006-
break
1007-
case 'show':
1008-
stacksRouter.get(`/${name}/:id`, `${handlerBase}ShowAction`)
1009-
break
1010-
case 'update':
1011-
stacksRouter.put(`/${name}/:id`, `${handlerBase}UpdateAction`)
1012-
break
1013-
case 'destroy':
1014-
stacksRouter.delete(`/${name}/:id`, `${handlerBase}DestroyAction`)
1015-
break
998+
const registerResourceRoutes = () => {
999+
for (const action of activeActions) {
1000+
switch (action) {
1001+
case 'index':
1002+
stacksRouter.get(`/${name}`, `${handlerBase}IndexAction`)
1003+
break
1004+
case 'store':
1005+
stacksRouter.post(`/${name}`, `${handlerBase}StoreAction`)
1006+
break
1007+
case 'show':
1008+
stacksRouter.get(`/${name}/:id`, `${handlerBase}ShowAction`)
1009+
break
1010+
case 'update':
1011+
stacksRouter.put(`/${name}/:id`, `${handlerBase}UpdateAction`)
1012+
break
1013+
case 'destroy':
1014+
stacksRouter.delete(`/${name}/:id`, `${handlerBase}DestroyAction`)
1015+
break
1016+
}
10161017
}
10171018
}
10181019

1019-
// Apply middleware to all resource routes if specified
1020+
// Wrap resource routes in a group if middleware is specified
10201021
if (options?.middleware) {
1021-
// Middleware is applied via the group mechanism
1022+
stacksRouter.group({ middleware: options.middleware }, registerResourceRoutes)
1023+
}
1024+
else {
1025+
registerResourceRoutes()
10221026
}
10231027

10241028
return stacksRouter
10251029
},
10261030

1031+
// Match multiple HTTP methods for a single route
1032+
match(methods: string[], path: string, handler: StacksHandler) {
1033+
for (const method of methods) {
1034+
const m = method.toUpperCase()
1035+
const { fullPath, routeKey } = registerRoute(m, path, handler)
1036+
const wrappedHandler = createMiddlewareHandler(routeKey, handler)
1037+
switch (m) {
1038+
case 'GET': bunRouter.get(fullPath, wrappedHandler); break
1039+
case 'POST': bunRouter.post(fullPath, wrappedHandler); break
1040+
case 'PUT': bunRouter.put(fullPath, wrappedHandler); break
1041+
case 'PATCH': bunRouter.patch(fullPath, wrappedHandler); break
1042+
case 'DELETE': bunRouter.delete(fullPath, wrappedHandler); break
1043+
case 'OPTIONS': bunRouter.options(fullPath, wrappedHandler); break
1044+
}
1045+
}
1046+
return createChainableRoute(`${methods[0]}:${currentPrefix}${path}`)
1047+
},
1048+
10271049
// Health check route
10281050
health() {
10291051
bunRouter.get('/health', () => Response.json({ status: 'healthy', timestamp: Date.now() }))
@@ -1069,21 +1091,31 @@ export function createStacksRouter(config: StacksRouterConfig = {}): StacksRoute
10691091

10701092
// Import routes from route registry
10711093
async importRoutes(): Promise<void> {
1094+
// Load user-defined routes
10721095
try {
1073-
// Load routes from the route registry
10741096
const { loadRoutes } = await import('./route-loader')
10751097
const routeRegistry = (await import('../../../../../app/Routes')).default
10761098
await loadRoutes(routeRegistry)
1099+
}
1100+
catch (error) {
1101+
log.error('Failed to load route registry:', error)
1102+
}
10771103

1078-
// Also load ORM routes
1104+
// Load ORM-generated API routes
1105+
try {
10791106
const ormRoutesPath = p.frameworkPath('core/orm/routes.ts')
10801107
await import(ormRoutesPath)
1108+
}
1109+
catch (error) {
1110+
log.debug('ORM routes not available:', error)
1111+
}
10811112

1082-
// Load routes from discovered packages
1113+
// Load routes from discovered packages
1114+
try {
10831115
await stacksRouter.loadDiscoveredRoutes()
10841116
}
10851117
catch (error) {
1086-
log.error('Failed to import routes:', error)
1118+
log.debug('Package route discovery skipped:', error)
10871119
}
10881120
},
10891121

@@ -1141,6 +1173,7 @@ export interface StacksRouterInstance {
11411173
options: (path: string, handler: StacksHandler) => ChainableRoute
11421174
group: (options: GroupOptions, callback: () => void | Promise<void>) => StacksRouterInstance | Promise<StacksRouterInstance>
11431175
resource: (name: string, handler: string, options?: ResourceRouteOptions) => StacksRouterInstance
1176+
match: (methods: string[], path: string, handler: StacksHandler) => ChainableRoute
11441177
health: () => StacksRouterInstance
11451178
use: (middleware: ActionHandler) => StacksRouterInstance
11461179
register: (routePath: string, options?: { prefix?: string, middleware?: string | string[] }) => Promise<StacksRouterInstance>

0 commit comments

Comments
 (0)