I set up a cronjob that is supposed to fire and call a service from another module. console logged items are displaying in the console and When I run the method manually from the endpoint. The service returns a successful result. But once I put back the cronjob decorator. The service is undefined
throwing exception TypeError: Cannot read property 'getAll' of undefined
I have used other nodejs cronjob packages, but the error persists. Is there a workaround?
#Cron(CronExpression.EVERY_10_SECONDS)
async test() {
try {
console.log('working 22');
const ee = await this.Service.getAll();
console.log(ee);
for (const key in ee) {
console.log(ee[key].termsID);
}
const terms = await this.termsModel.find({
isDeleted: false
});
console.log(terms);
console.log('working 22 end!');
} catch (error) {
console.log(error)
}
}
appmodule
#Module({
imports: [
TermsModule,
ScheduleModule.forRoot()
],
controllers: [],
providers: [],
})
export class AppModule { }
You need to make sure that you declare the service that you want to use from the global module in the Cron-Service's providers. Consider this simple example:
// Sample Cron-Service
// -------------
#Injectable()
export class CronService {
private readonly logger = new Logger(CronService.name);
constructor(private globalService: GlobalService) {
}
#Cron(CronExpression.EVERY_5_SECONDS)
test() {
this.logger.debug(`Called every 5 seconds with random value: ${this.globalService.getSomeData()}`);
}
}
// Cron-Module
// -------------
#Module({
providers: [CronService, GlobalService] // <--- this is important, you need to add GlobalService as a provider here
})
export class CronModule {
}
// Global-Service
// -------------
#Injectable()
export class GlobalService {
getSomeData() {
return Math.random() * 500;
}
}
// Global-Module
// -------------
#Global()
#Module({
providers: [GlobalService]
})
export class GlobalModule {
}
Also, you need to make sure that the global module is imported in your root/core module - along with the ScheduleModule from the #nestjs/schedule package, e.g.:
#Module({
imports: [GlobalModule, ScheduleModule.forRoot(), ... ]
})
export class AppModule {
}
Related
I'm using pino-logger in my NestJS project to log the activities in my application, and I'm logging the object along with ReqId so I can trace the whole activity inside one request. I'd like to use the same "ReqId" in another place as well, but I'm unsure of how to move it outside of the module, so for that, I'm thinking to save that generated ReqId into the CacheManager but not sure how to inject CacheManager class inside genReqId function. Please look over the code below.
app.module.ts
#Module({
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
// I'm thinking to use CacheManager here but I'm not sure how to inject CacheManager class here
return req.headers.req_id || uuid(); // from here it generate the request ID and I want to export this ID and use in side an another class
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
})
export class AppModule {}
you need To create sharable service and import it Imports
#Injectable()
export class RequestIdService {
private reqId: string;
setRequestId(reqId: string) {
this.reqId = reqId;
}
getRequestId() {
return this.reqId;
}
}
than import it to logger module
imports: [
LoggerModule.forRoot({
pinoHttp: {
genReqId: (req: any) => {
this.requestIdService.setRequestId(req.headers.req_id || uuid());
return this.requestIdService.getRequestId();
},
base: undefined,
quietReqLogger: true,
timestamp: false,
},
}),
],
providers: [RequestIdService],
```
use that service by
import { RequestIdService } from './request-id.service';
this.requestIdService.getRequestId()
I am trying to inject a dependency to an exception filter.
Here is my dependency:
#Injectable()
export class SmsService {
constructor() {}
async create() {
console.log('sms created');
}
}
And its module:
#Module({
providers: [SmsService],
exports: [SmsService],
})
export class SmsModule {}
And my exception filter is here:
#Catch(InternalServerErrorException)
export class InternalServerErrorFilter implements ExceptionFilter {
#Inject(SmsService)
private readonly smsService: SmsService;
async catch(exception: InternalServerErrorException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
// smsService is undefined so create property is undefined
await this.smsService.create();
response.status(exception.getStatus()).send({
message: 'Something went wrong our side.',
statusCode: exception.getStatus(),
});
}
}
My app module :
#Module({
imports: [SmsModule, MailsModule],
controllers: [AppController],
providers: [
AppService,
{
provide: APP_FILTER,
useClass: InternalServerErrorFilter,
},
],
})
export class AppModule {}
My main.ts file :
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalFilters(new InternalServerErrorFilter());
await app.listen(3000);
}
bootstrap();
Finally my app service is here.
#Injectable()
export class AppService {
getHello(): string {
throw new InternalServerErrorException();
}
}
It just throws internal server error exception for executing internal server error filter.
When I send a request to http://localhost:3000 it throws an error like below:
/* await this.smsService.create();
^
TypeError: Cannot read properties of undefined (reading 'create')
*/
My application starts successfully so it seems like no dependency error.
If I recall, enhancers bound by useGlobal*() have precedence over APP_* providers (need to double check that). By using new you are in charge of setting everything that that class instance will need, regardless if it has #Inject() decorators on properties or in the constructor or not. The #Inject() just sets metadata that Nest can read so it knows what to set during class instantiation. So, when you pass new InternalServerErrorFilter() and don't ever set smsService, you get an error at runtime because that service is never defined, only declared.
If you're going to use global enhancers, I'd highly suggest keeping just one global enhancer binding type, and would even more highly suggest just using the APP_* bindings because they're easier to keep inline with your application and your e2e tests, plus Nest can do DI on enahncers bound via APP_*
I want to save some data out of a body and use it with the next request in my tests. I'm doing a post request and get back an id. This id I want to use to fetch data in other test.
My code looks like this:
it('/auth/register (POST)', async () => {
const test = await request(app.getHttpServer())
.post('/api/auth/register')
.send({username: "Zoe"})
.expect(201)
token = test.body.token
})
The token is getting set and the code runs, however my jest test won't stop with and I'll get the error:
Jest did not exit one second after the test run has completed.
This usually means that there are asynchronous operations that weren't stopped in your tests. Consider running Jest with `--detectOpenHandles` to troubleshoot this issue.
I know that I can do forceExit but I want to do it clean and understand the problem.
When I do the jest return method, it's working.. However there Idk how to store the body somewhere..
it('/auth/register (POST)', () => {
request(app.getHttpServer())
.post('/api/auth/register')
.send({username: "Zoe"})
.expect(201)
})
I resolved this problem by closing the database connection when the application is shutting down
import { Module } from '#nestjs/common';
import { getConnection } from 'typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseModule } from './database/database.module';
import { FormsModule } from './forms/forms.module';
#Module({
imports: [
DatabaseModule,
FormsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {
onApplicationShutdown(){
getConnection().close();
}
}
```
I get this error message when i import another module service in DoctorsService:
Nest can't resolve dependencies of the AdminsService (?). Please make
sure that the argument AdminsRepository at index [0] is available in
the DoctorsModule context.
I imported AdminsService in doctors.module in provider, but problem not solved.
doctors.module.ts
#Module({
imports: [PassportModuleJwt],
controllers: [DoctorsController],
providers: [DoctorsService, AdminsService],
})
admins.module.ts
#Module({
imports: [PassportModuleJwt, TypeOrmModule.forFeature([AdminsRepository])],
controllers: [AdminsController],
providers: [AdminsService],
exports: [AdminsService],
})
And finally this is my doctors.service and here the error occurs:
#Injectable()
export class DoctorsService {
constructor(private adminsService: AdminsService) {}
async create(createDoctorDto: CreateDoctorDto): Promise<DoctorPayload> {
const { user_id, name, avatar, bio } = createDoctorDto;
await this.adminsService.findOne(user_id);
const doctor = new Doctor();
doctor.user_id = user_id;
doctor.name = name;
doctor.avatar = avatar;
doctor.bio = bio;
try {
return await doctor.save();
} catch (error) {
throw new InternalServerErrorException();
}
}
}
and my admins.service:
#Injectable()
export class AdminsService {
constructor(
#InjectRepository(AdminsRepository)
private adminsRepository: AdminsRepository,
) {}
async findOne(id: number): Promise<Admin> {
const admin = await this.adminsRepository.findById(id);
if (!admin) {
throw new NotFoundException();
}
delete admin.password;
return admin;
}
}
Whats wrong?
AdminsService is exported from AdminsModule. To use it inside another module, you need to import it.
// doctors.module.ts
#Module({
imports: [PassportModuleJwt, AdminsModule], // Add AdminsModule here
controllers: [DoctorsController],
providers: [DoctorsService], // Remove AdminsService here
})
Note that DoctorsModule should not provide AdminsService, as its already provided by AdminsModule.
Using the process outlined here, I'm trying to inject Angular 1 services into an Angular 4 app. The app is bootstrapped in hybrid mode (and works as I have some Angular 4 components and services running).
Whenever I try to inject the Angular 1 service, I get Cannot read property 'get' of undefined.
upgraded-providers.ts:
import {LinksService} from "./+services/links/links";
export function linksServiceFactory(i: any) {
return i.get('bfLinksService'); // <--- Errors here!
}
export const linksServiceProvider = {
provide: LinksService,
useFactory: linksServiceFactory,
deps: ['$injector']
};
My Angular 4 service which is trying to use LinksService looks like:
#Injectable()
export class EntityService {
constructor (
private http: Http,
private links: LinksService
) {
}
get(id: string): Observable<OrgDetails> {
// Do something
}
}
And finally LinksService (the Angular 1 service, written in Typescript) looks like:
export class LinksService {
static $inject = ["$log", "$location", PROPERTIES_SERVICE];
private contentHost : string;
private thisAppHost : string;
constructor (private $log : ng.ILogService, private $location : ng.ILocationService, private props : IPropertiesService) {
this.init();
}
// Service functions elided
}
The bootstrap and module stuff:
#NgModule({
imports: [
BrowserModule,
HttpModule,
UpgradeModule,
],
declarations: [
AppComponent,
OrgSummaryComponent,
],
providers: [
EntityService,
linksServiceProvider
],
bootstrap: [
AppComponent,
],
})
export class AppModule {
ngDoBootstrap() {
// Does nothing by design.
// This is to facilitate "hybrid bootstrapping"
}
}
platformBrowserDynamic().bootstrapModule(AppModule).then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, [AppModuleName], {strictDi: false});
});
The Angular 1 (legacy) stuff all works fine.
It seems like Angular cant find the $injector, but shouldn't that be there regardless?
Many thanks for any suggestions,
Jeff
Two days of my life I won't get back but...
Just found this:
https://github.com/angular/angular.io/issues/3317
Basically the documentation is wrong. By adding a constrcutor to the app module with the call to upgrade.bootstrap in it, everything works.
export class AppModule {
constructor(upgrade: UpgradeModule) {
upgrade.bootstrap(document.body, [AppModuleName], {strictDi: true});
}
// Does nothing by design.
// This is to facilitate "hybrid bootstrapping"
ngDoBootstrap() {}
}
platformBrowserDynamic().bootstrapModule(AppModule);
Thank you to those who responded.
Actually the better way to instantiate AngularJS is after:
platformBrowserDynamic().bootstrapModule(AppModule)
.then(platformRef => {
const upgrade = platformRef.injector.get(UpgradeModule) as UpgradeModule;
upgrade.bootstrap(document.body, ['app'], { strictDi: false });
})
.catch(err => console.log(err));