Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.vscode/launch.json
10 changes: 5 additions & 5 deletions lib/internals/findRoute.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,6 @@ class Handler {
return { list: true };
}

@Get('/:id(\\d+)')
public details() {
return { details: true };
}

@Get('/item', { extraMethods: [HttpMethod.OPTIONS] })
public item() {
return { item: true };
Expand All @@ -23,6 +18,11 @@ class Handler {
public childItem() {
return { childItem: true };
}

@Get('/:id')
public details() {
return { details: true };
}
}

describe('findRoute', () => {
Expand Down
10 changes: 6 additions & 4 deletions lib/internals/findRoute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,18 @@ export function findRoute(
];
}

const keys: Key[] = [];
let keys: Key[] = [];
let match: RegExpExecArray | null | undefined;
const method = methods.find(f => {
match = pathToRegexp(f.path, keys).exec(path);
const method = methods.reverse().find(f => {
const { regexp, keys: keysTmp } = pathToRegexp(f.path);
keys = keysTmp;
match = regexp.exec(path);

const condition =
(f.method === requestMethod || f.options?.extraMethods?.includes(requestMethod as HttpMethod)) && match?.length;

if (!condition) {
keys.length = 0;
keys = [];
match = undefined;
}

Expand Down
65 changes: 49 additions & 16 deletions lib/internals/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,32 +21,57 @@ function isResponseSent(res: ServerResponse): boolean {
}

async function runMiddlewares(
this: TypedPropertyDescriptor<any>,
middlewares: Middleware[],
req: NextApiRequest,
res: NextApiResponse
res: NextApiResponse,
callback: () => Promise<void>
): Promise<void> {
for (const middleware of middlewares) {
const executeMiddleware = async (
req: NextApiRequest,
res: NextApiResponse,
index: number,
next: (err: Error | null) => void
) => {
if (isResponseSent(res)) {
break;
next(null);
return;
}
if (index === middlewares.length) {
// Base case: all middlewares have been executed
next(null);
return;
}

await new Promise<void>((resolve, reject) => {
// The middleware uses the callback.
const fnResult = (middleware as NextMiddleware).call(this, req, res, err => {
const middleware = middlewares[index] as NextMiddleware;

try {
await middleware(req, res, async err => {
if (err) {
return reject(handleMulterError(err));
// If an error occurs, stop execution and propagate the error back up the call stack
next(handleMulterError(err));
} else {
// If no error occurs, execute the next middleware
await executeMiddleware(req, res, index + 1, next);
}

resolve();
});
} catch (err) {
// If an error occurs, stop execution and propagate the error back up the call stack
next(handleMulterError(err as Error));
}
};

// The middleware is async.
if (fnResult instanceof Promise) {
fnResult.then(resolve).catch(reject);
// Start executing the first middleware
await new Promise<void>((resolve, reject) => {
executeMiddleware(req, res, 0, (err: Error | null) => {
if (err) {
// Handle any errors that occur during middleware execution
reject(err);
} else {
// All middlewares have been executed successfully
callback().then(resolve).catch(reject);
}
});
}
});
}

async function runMainLayer(
Expand Down Expand Up @@ -157,8 +182,16 @@ export function applyHandler(
);

try {
await runMiddlewares.call(this, [...(classMiddlewares ?? []), ...(methodMiddlewares ?? [])], req, res);
await runMainLayer.call(this, target, propertyKey, originalHandler, req, res);
const runMainLayerWrapper = async () => {
await runMainLayer.call(this, target, propertyKey, originalHandler, req, res);
};
await runMiddlewares.call(
this,
[...(classMiddlewares ?? []), ...(methodMiddlewares ?? [])],
req,
res,
runMainLayerWrapper
);
} catch (err) {
if (isResponseSent(res)) {
return;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"lint-staged": "13.1.0",
"multer": "^1.4.2",
"next": "13.1.1",
"path-to-regexp": "^6.2.0",
"path-to-regexp": "^8.0.0",
"pinst": "^3.0.0",
"prettier": "2.2.1",
"react": "^18.0.0",
Expand Down
2 changes: 1 addition & 1 deletion test/e2e-basic-routing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class ArticleHandler {
return ARTICLES;
}

@Get('/:id(\\d+)')
@Get('/:id')
public article(@Param('id', ParseNumberPipe) id: number) {
return ARTICLES.find(article => article.id === id);
}
Expand Down
Loading