Skip to content

Add Feature expansion .use(path, middleware)#3044

Closed
Octo8080X wants to merge 16 commits intodenoland:mainfrom
Octo8080X:feature/add_middleware_with_path
Closed

Add Feature expansion .use(path, middleware)#3044
Octo8080X wants to merge 16 commits intodenoland:mainfrom
Octo8080X:feature/add_middleware_with_path

Conversation

@Octo8080X
Copy link
Contributor

@Octo8080X Octo8080X commented Jun 16, 2025

target: #3040

Currently in progress.

@Octo8080X Octo8080X changed the title Feature/add middleware with path Add Feature expansion .use(path, middleware) Jun 16, 2025
Copy link
Contributor

@iuioiua iuioiua left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick pass

src/router.ts Outdated
export interface Route<T> {
path: string | URLPattern;
method: Method | "ALL";
method: Method | "ALL" | "MIDDLEWARE";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems odd to me. Is there an alternative?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there’s a way to use “ALL” instead of “MIDDLEWARE”.
Or we could allow undefined, but I feel that would have too large of an impact.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to go with "ALL".

src/app.ts Outdated
Comment on lines +255 to +261
const middlewareHandlers: Array<MiddlewareFn<State>> = [];
for (const middleware of middlewares) {
for (const handler of middleware.handlers) {
// We need to safely cast here to handle the type properly
middlewareHandlers.push(handler as unknown as MiddlewareFn<State>);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const middlewareHandlers: Array<MiddlewareFn<State>> = [];
for (const middleware of middlewares) {
for (const handler of middleware.handlers) {
// We need to safely cast here to handle the type properly
middlewareHandlers.push(handler as unknown as MiddlewareFn<State>);
}
}
const middlewareHandlers = middlewares.flatMap((middleware) =>
middleware.handlers
);

Comment on lines +263 to +267
// Create a new array of handlers with the correct type
const combinedHandlers: Array<MiddlewareFn<State>> = [
...middlewareHandlers,
...route.handlers.map((h) => h as unknown as MiddlewareFn<State>),
];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Create a new array of handlers with the correct type
const combinedHandlers: Array<MiddlewareFn<State>> = [
...middlewareHandlers,
...route.handlers.map((h) => h as unknown as MiddlewareFn<State>),
];
const combinedHandlers = [
...middlewareHandlers,
...route.handlers,
];

src/app.ts Outdated
Comment on lines +179 to +190
if (
typeof pathOrMiddleware === "string" ||
pathOrMiddleware instanceof URLPattern
) {
// First argument is a path
if (!middleware) {
throw new Error("Middleware is required when path is provided");
}
this.#router.addMiddleware(pathOrMiddleware, middleware);
} else {
this.#router.addMiddleware("/*", pathOrMiddleware);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (
typeof pathOrMiddleware === "string" ||
pathOrMiddleware instanceof URLPattern
) {
// First argument is a path
if (!middleware) {
throw new Error("Middleware is required when path is provided");
}
this.#router.addMiddleware(pathOrMiddleware, middleware);
} else {
this.#router.addMiddleware("/*", pathOrMiddleware);
}
if (typeof pathOrMiddleware === "function") {
this.#router.addMiddleware("/*", pathOrMiddleware);
return this;
}
if (!middleware) {
throw new TypeError("Middleware is required when path is provided");
}
this.#router.addMiddleware(pathOrMiddleware, middleware);

Maybe a little tidier?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you.
With the suggested approach, pathOrMiddleware can't be narrowed down to string | URLPattern, which causes a type error—so I decided to keep it as it is for now.

src/router.ts Outdated
Comment on lines +36 to +51
if (
typeof pathname === "string" && pathname !== "/*" &&
IS_PATTERN.test(pathname)
) {
this._middlewares.push({
path: new URLPattern({ pathname }),
handlers: [handler],
method: "MIDDLEWARE",
});
} else {
this._middlewares.push({
path: pathname,
handlers: [handler],
method: "MIDDLEWARE",
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (
typeof pathname === "string" && pathname !== "/*" &&
IS_PATTERN.test(pathname)
) {
this._middlewares.push({
path: new URLPattern({ pathname }),
handlers: [handler],
method: "MIDDLEWARE",
});
} else {
this._middlewares.push({
path: pathname,
handlers: [handler],
method: "MIDDLEWARE",
});
}
this._middlewares.push({
path: isPattern(pathname) ? new URLPattern({ pathname }) : pathname,
handlers: [handler],
method: "MIDDLEWARE",
});

We can factor out that conditional into an isPattern() which can be reused on lines 60-61.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!
I extracted it as isURLPattern.

src/router.ts Outdated
Comment on lines +55 to +58
path: (
typeof pathname === "string" && pathname !== "/*" &&
!this.isURLPattern(pathname)
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: To clarify, my idea was to have everything inside this condition be factored out. Not just the URLPattern bit. Also, I don't see a reason why it should be a method when it doesn't use any property from this class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies — I didn’t fully grasp your intent at first.
I’ve now moved the logic out of the class and reorganized the content.

Copy link
Contributor

@iuioiua iuioiua left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Just a few non-blocking nits.

src/app.ts Outdated
this.#router.addMiddleware("/*", pathOrMiddleware);
} else {
if (!middleware) {
throw new Error("Middleware is required when path is provided");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
throw new Error("Middleware is required when path is provided");
throw new TypeError("Middleware is required when path is provided");

Nit

src/app.ts Outdated
: middlewares.concat(route.handlers);
this.#router.add(route.method, merged, combined);
// Create a new array of handlers with the correct type
const combinedHandlers: Array<MiddlewareFn<State>> = [
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const combinedHandlers: Array<MiddlewareFn<State>> = [
const combinedHandlers = [

Nit: this might not be needed

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice tests!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

src/router.ts Outdated
method: Method | "ALL",
pathname: string | URLPattern,
handlers: T[],
): void {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
): void {
) {

Nit: the Deno team consider defining void and Promise<void> as explicit return types as needless boilerplate.

@marvinhagemeister
Copy link
Collaborator

Thanks for working on a PR. We've refactored the internals of Fresh quite a bite to have sort of an internal route tree when building up the middlewares. This was needed to get other features in that we needed, but does unfortunately mean that the functionality proposed in this PR has already been implemented, see #3086

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants