How can I pass configuration object into forRoot() in AppModule? Is there a way to access configService if there is not constructor in the module?
config.yml:
smtp:
host: 'smtp.host.com'
port: 10
secure: true
auth:
user: 'username'
auth: 'password'
#Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
MailModule.forRoot(), // I want to pass configuration here
],
})
export class AppModule {}
Usually you'd need an async registration method like forRootAsync, but it's up to the module creator to provide this method. If this method does exist you can use a factory and inject to create a class-like provider system that Nest can inject with as shown in the database docs. Without knowing which mailing module you're working with, I can't say more if it supports this or not.
Without that async registration method, it's not possible to use DI in the forRoot.
I installed config package and used it like so:
import * as config from 'config';
const smtpConfig = config.get('smtp');
#Module({
imports: [
MailModule.forRoot(smtpConfig),
],
})
export class AppModule {}
Related
I have two auth middleware in my nestjs project.
AdminAuth Middleware
UserAuth Middleware
AdminAuthMiddleware will be used in AdminModule while UserAuthMiddleware will be used in rest of the modules.
export class AppModule implements NestModule {
static register(option: DynamicModuleOptionType): DynamicModule {
return {
module: AppModule,
imports: [
BullQueueModule.register(option),
KafkaModule.register(option),
CronModule.register(option),
],
};
}
configure(consumer: MiddlewareConsumer) {
consumer.apply(CorsMiddleware).forRoutes('*');
consumer.apply(AdminAuthMiddleware).forRoutes('/v1/admin/(.*)');
consumer
.apply(UserAuthMiddleware)
.exclude(
'v1/admin/(.*)',
'/_livez',
'/_healthz',
'/_readyz',
'/swagger.json',
)
.forRoutes('*');
}
}
UserAuthMiddleware middleware is working correctly, but AdminAuthMiddleware is not registering for admin routes.
How can i solve this issue?. Any help will be highly appreciated.
I tried registering AdminAuthMiddleware in AdminModule only, it did not work.
Tried changing the sequence of middleware registration.your text
I was wondering if there is a method to access directly the session memory store and get the session data for a particular session id i'm using express-session package with the nest js freamwork.
Hope you found answers to your question since then. I came up with the following technique which is using NestJS DI system.
Background
According to NestJS official session documentation you can register a session middleware inside your main.ts file.
According to ExpressJS official session middleware documentation (the session(options) on top of the page), you can specify the session store that will be used by the middleware. If its omitted then the default MemoryStore is used.
All the session stores shares a common interface, so its a interchangeable part of your application, which makes a good candidate them to be provided as an injection token by a module. See a list of session stores advised by expressjs.
Implementation details
Create a new module (e.g. SessionModule), then add it to the imports array in your AppModule.
Next we create the injection token for the session store.
import { MemoryStore } from 'express-session';
import { SESSION_STORE } from './session.const';
#Module({
providers: [
{
provide: SESSION_STORE,
useFactory: () => {
return new MemoryStore();
},
},
],
exports: [SESSION_STORE],
})
export class SessionModule {}
where session.const.ts contains a simple export statement
export const SESSION_STORE = 'SESSION_STORE';
Now inside main.ts file, you can get a reference to the session store to setup session middleware.
const store = app.get(SESSION_STORE)
app.use(express.session({
store,
// ... other options
}
));
If you want to access sessionStore in other services / controllers you have to import the SessionModule to the module that declares them.
ex.
#Module({
imports: [SessionModule],
controllers: [UserController]
})
export class UserModule {}
// inside user controller you can inject a reference
// to the session store in the constructor
#Controller('user')
export class UserController {
constructor(#Inject(SESSION_STORE) private sessionStore) {}
}
With the sessionStore property you can get any stored session with the following method documented in the linked expressjs documentation. store.get(sid, callback)
I am using a NestJS + MikroORM stack and trying to write tests using Jest.
On the user.service.spec.ts I am always getting the following error:
Nest can't resolve dependencies of the UserService (?). Please make sure that the argument UserRepository at index [0] is available in the RootTestModule context
The user.service.spec.ts:
describe('UserService', () => {
let userService: UserService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: {
find: jest.fn().mockResolvedValue([]),
findOneOrFail: jest.fn().mockResolvedValue({}),
create: jest.fn().mockReturnValue({}),
save: jest.fn(),
update: jest.fn().mockResolvedValue(true),
delete: jest.fn().mockResolvedValue(true),
},
},
],
}).compile();
userService = module.get<UserService>(UserService);
});
it('should be defined with dependencies', () => {
expect(userService).toBeDefined();
});
});
The user.repository.ts:
#Repository(User)
export class UserRepository extends EntityRepository<User> {}
Why would that be happening? According to all other tutorials, it should work. Thanks.
if your UserService's constructor has
private readonly repo: UserRepository
then you should use provide: UserRepository because now your provider's token is a class references, not its name.
Nest 8 changed the way DI works, it was using string tokens before, but now it uses class references instead. The nest MikroORM adapter does register both string token and class reference for custom repositories. Here you are registering the repository yourself, so you either need to register it both ways or at least the way you use.
Importing via the type requires the class reference way. Importing via #InjectRepository() requires the string token. forFeature() call registers them both in case the entity has a custom repository class.
https://github.com/mikro-orm/nestjs/blob/e51206762f9eb3e96bfc9edbb6abbf7ae8bc08a8/src/mikro-orm.providers.ts#L82-L94
So either add the provide: UserRepository as suggested in the other answer, or use #InjectRepository() decorator.
Cannot read set property of 'userId' of undefined is a classic error that has been experienced in Express frameworks and documentation here covers what to do about it, but how do you resolve this issue in a NestJS application?
When you get an error message that says Cannot set property 'userId' of undefined you want to look at what is going on with your cookie-session.
Do you have a cookie-session installed?
The error is being thrown whenever you try to set the user id property on the users' session object, because that cookie middleware is not installed or it's not running, you get undefined.
So when you try to set a property on undefined, you get this classic error message.
So your cookie-session may not be set up.
Enough answers were given in a plain ExpressJS API, but what if you are working with NestJS? Well, here is the NestJS way to resolve this.
Import the following to your app.module.ts file:
import { Module, ValidationPipe } from '#nestjs/common';
import { APP_PIPE } from '#nestjs/core';
Go down to your list of providers and inside the array and a brand new object:
providers: [
AppService,
{
provide: APP_PIPE,
useValue: new ValidationPipe({
whitelist: true,
}),
},
],
So what does this really do? It says whenever we create an instance of the app module, automatically make use of it. Apply it to every incoming request that flows to the application, run it through the instance of a class. That's how to set up a global pipe, but you have to set up the cookie session middleware into a global middleware.
You need to import the following to the same file:
import { MiddlewareConsumer, Module, ValidationPipe } from '#nestjs/common';
At the bottom, add the following:
export class AppModule {
configure(consumer: MiddlewareConsumer) {}
}
The configure function gets called automatically whenever the application starts listening for incoming traffic. So inside of here I can set up some middleware that will run on every single incoming request.
To do, we call or reference consumer.apply() like so:
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(
cookieSession({
keys: ['dfghjkl'],
}),
);
}
}
I then need to ensure I add in a require statement for cookie session at the top:
const cookieSession = require('cookie-session');
And at the bottom also add:
export class AppModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
cookieSession({
keys: ['dfghjkl'],
}),
)
.forRoutes('*');
}
}
That means that I want to make use of the middleware on every single incoming request that comes into the application. That should be it.
I've been developing a wrapper module for nestjs based on a nodejs module. I created a static forRoot method in order to get the configuration. I created such a prodiver within the forRoot method:
const MyProvider = {
provide: PROVIDER_TOKEN,
useValue: new MyClass(options),
};
I also export it, so in consumer module it's easy to inject it in order to access to all methods of nodejs module. Besides, I am able to wrap up all methods of that module into my service methods. So, the following code give me access to the main module's instance:
constructor(#Inject(PROVIDER_TOKEN) private readonly myClass: MyClass) {}
Then I decided to create a forRootAsync method that can handle getting configuration with useFactory. Now this is my provider in forRootAsync method:
const MyProvider= {
provide: PROVIDER_TOKEN,
useFactory: optionsAsync.useFactory,
inject: optionsAsync.inject || []
};
But this time if I inject PROVIDER_TOKEN to the service, this is simply the configuration object (that I pass from the consumer module). So I guess I should create the instance within constructor. Maybe something like this:
constructor(#Inject(PROVIDER_TOKEN) private readonly myClass) {
if(!this.myClass typeof MyClass) {
this.myClass = new MyClass(this.myClass);
}
}
By this, I can't access the instance of the main module in the consumer modules by injecting PROVIDER_TOKEN token. The goal is to access all methods of that module without having to wrap all the methods up. Any idea?
We should handle this with two providers. In the first one, we pass the factory provider as following:
{
provide: HTTP_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
Then we create another provider which injects the first provider (which nestjs resolve the dependency at that point):
{
provide: AXIOS_INSTANCE_TOKEN,
useFactory: (config: HttpModuleOptions) => Axios.create(config),
inject: [HTTP_MODULE_OPTIONS],
},
Here is the example.