feat: Mount non-dynamic routes first
Description
Currently dynamic directory routes get mounted first which leads to other routes in the same directory never getting matched.
For example, when you have routes like routes/api/[id]/index.dart and routes/api/status.dart the latter can never get matched. The generated code will always mount the first route first so the router will always pass status to [id].
Additional Context
Here is the root handler generated by dart_frog with my current project which implements the Mastodon API. Routes for the second mount such as/api/v1/accounts/search can never be reached.
Handler buildRootHandler() {
final pipeline = const Pipeline().addMiddleware(middleware.middleware);
final router = Router()
..mount('/api/v1/accounts/<id>', (context,id,) => buildApiV1Accounts$idHandler(id,)(context))
..mount('/api/v1/accounts', (context) => buildApiV1AccountsHandler()(context))
..mount('/api/v1/timelines', (context) => buildApiV1TimelinesHandler()(context))
..mount('/api/v1', (context) => buildApiV1Handler()(context))
..mount('/oauth', (context) => buildOauthHandler()(context))
..mount('/', (context) => buildHandler()(context));
return pipeline.addHandler(router);
}
Hi @videah, thanks for opening this issue!
I haven't attempted to reproduce this yet. Regardless, would you possibly be interested in contributing with a PR?
I would take a stab at it but I'm not entirely sure how I go about creating a local dev environment for dart_frog 😅
I was able to reproduce this using dart_frog_cli version 0.3.6, see this for the full sample.
Demo video
https://github.com/VeryGoodOpenSource/dart_frog/assets/44524995/14ee63bb-5180-44b9-8146-a62f3dfd34d2
For reference, we should definitely support this.
Here is a section of the OpenApi specification where the behaviour is mentioned.
@videah unfortunately, changing the order of the mounting of the routes in such a proposed way will not be able at the moment.
We have spent time working on implementing a different order to make it possible for non-dynamic and dynamic routes that would listen to similar endpoint be able to respond to their specific urls, but we faced a couple of issues. We actually got the precedence to work as expected, but that generated complicated side effects for middlewares.
Since both GET routes/api/status and GET routes/api/XXXX would be able to handled from routes/api/[id]/index.dart and given the complications mentioned above we decided to keep as is and implement a conflict detection (#687), so at least, the developer will be notified of the conflict and issues will not go unnoticeable.
Now that we have the validation in place, I will mark this as an enhancement so we can try to come back to it in there future after we had the code gen restructure.