nest
nest copied to clipboard
Middleware not executed when using `exclude` in `setGlobalPrefix`
Is there an existing issue for this?
- [X] I have searched the existing issues
Current behavior
In versions 10.3.4+, if using exclude
in setGlobalPrefix
, e.g.
app.setGlobalPrefix('api', { exclude: ['/'] });
Then middleware stops being executed at all (any sort of middleware).
For reproduction see tests in https://github.com/xtrinch/nestjs-middleware-issue-demo
Minimum reproduction code
https://github.com/xtrinch/nestjs-middleware-issue-demo
Steps to reproduce
- Run
yarn test
, observe that the tests do not pass - middleware should attach a header to request - Remove the
exclude
param fromsetGlobalPrefix
- Run tests again and observe that the tests now pass - middleware attaches a header to request
Expected behavior
Middleware should run regardless of exclude in setGlobalPrefix
Package
- [ ] I don't know. Or some 3rd-party package
- [ ]
@nestjs/common
- [X]
@nestjs/core
- [ ]
@nestjs/microservices
- [ ]
@nestjs/platform-express
- [ ]
@nestjs/platform-fastify
- [ ]
@nestjs/platform-socket.io
- [ ]
@nestjs/platform-ws
- [ ]
@nestjs/testing
- [ ]
@nestjs/websockets
- [ ] Other (see below)
Other package
No response
NestJS version
10.3.4+
Packages versions
{
"name": "test-project",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/core": "10.3.7",
"@nestjs/platform-fastify": "^10.3.7",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.42.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"source-map-support": "^0.5.21",
"supertest": "^6.3.4",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
Node.js version
18
In which operating systems have you tested?
- [X] macOS
- [ ] Windows
- [ ] Linux
Other
No response
I have tested the reproduction and confirmed that the issue is only present with FastifyAdapter
.
So the issue is most likely related to this fix: https://github.com/nestjs/nest/pull/13337
I think this is caused by https://github.com/nestjs/nest/pull/11832, which was intended to address the issue of middleware being called multiple times. However, it filters out some paths. This is effective for Express, but not for Fastify
Currently still affected by this even with latest nestjs (10.3.8) when using the mikro-orm module
I wonder if reverting this PR #11832 would fix your issue @eric-deeporigin
Would you like to try applying an inline patch just to test it out locally? (open node_modules and revert that change to see if it's causing issues for your project)
I wonder if reverting this PR #11832 would fix your issue @eric-deeporigin
Would you like to try applying an inline patch just to test it out locally? (open node_modules and revert that change to see if it's causing issues for your project)
@kamilmysliwiec Sorry for the late reply. I did test your changes out, but unfortunately it did not work.
My change looks like this in the core/middleware/middleware-module.js file in my node_modules folder of the app
Because we are in a monorepo, I had to find all the places where the middleware-module.js was defined and make sure all of them were replaced with the below changes. These are the files I had to update:
./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/middleware/middleware-module.js
./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/middleware/middleware-module.js
./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@nestjs+platform-expre_sn6fu2jetslishj5vuxdktzjei/node_modules/@nestjs/core/middleware/middleware-module.js
./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected]_@nestjs+platform-expre_jpofokt5mcym25avs3ht3bnhbi/node_modules/@nestjs/core/middleware/middleware-module.js
./node_modules/.pnpm/@[email protected]_@[email protected]_@[email protected][email protected][email protected]/node_modules/@nestjs/core/middleware/middleware-module.js
(we use NX with pnpm)
The change:
paths.forEach(path => router(path, middlewareFunction));
// const pathsToApplyMiddleware = [];
// paths.some(path => path.match(/^\/?$/))
// ? pathsToApplyMiddleware.push('/')
// : pathsToApplyMiddleware.push(...paths);
// pathsToApplyMiddleware.forEach(path => router(path, middlewareFunction));
Since the rest of the changes were testing/integration changes, I did not revert them.
I will work on a minimal repro shortly
@kamilmysliwiec Here is a reproduction of the bug. It uses MikroORM and GraphQL.
https://github.com/eric-deeporigin/nestjs-global-prefix-bug
To be clear, the error that is seen is this:
[Nest] 18210 - 06/17/2024, 7:15:25 PM ERROR [ExceptionsHandler] Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead.
ValidationError: Using global EntityManager instance methods for context specific actions is disallowed. If you need to work with the global instance's identity map, use `allowGlobalContext` configuration option or `fork()` instead.
Any endpoint that hits a GQL resolver and uses mikro-orm will throw this error.
If a GQL resolver does not use mikro-orm, the error will not be present.
Any endpoint that does not hit a GQL resolver (ie, an HTTP/Rest endpoint) does not have this issue.
I have followed the instructions specified here on mikro-orm's documentation when using gql + nest + mikro-orm: https://mikro-orm.io/docs/usage-with-nestjs#request-scoping-when-using-graphql
The issue goes away when not using app.setGlobalPrefix('api')
@eric-deeporigin I'd suggest you to report this to MikroORM repo as well.
@eric-deeporigin I'd suggest you to report this to MikroORM repo as well.
This has been reported on our end many times, no need for yet another report I can't do anything about, it needs to be fixed in nest.
https://github.com/nestjs/nest/issues/11572#issuecomment-1873927604
@kamilmysliwiec any update on this? This is also happening with Express adapter.
@xtrinch
i changed pattern of middleware .forRoutes({ path: '/', method: RequestMethod.ALL });
main.ts
app.setGlobalPrefix('/api/v1', {
exclude: [
{ path: '/', method: RequestMethod.GET },
{ path: '/api', method: RequestMethod.GET },
.....
],
});
my app.module.ts
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(RequestMiddleware)
.forRoutes({ path: '*/*', method: RequestMethod.ALL });
}
I think it still a bug, there will be a vul, which i cannot test. maybe help
@eric-deeporigin @B4nan hi, I tried the reproduction @eric-deeporigin provided, and I found that adding the { exclude: ['/graphql'] }
option to setGlobalPrefix
resolves the issue.
app.setGlobalPrefix('api', { exclude: ['/graphql'] });
This is because the GraphQL module bypasses the setGlobalPrefix
, and NestJS is unaware of the /graphql
path, resulting in the MikroOrmMiddleware
not being mounted on /graphql
. Iām not very familiar with mikro-orm, this is my guess after testing.