Nest: It can be nice to have access of the NestContainer instance of the application on the modules

Created on 18 Oct 2017  路  18Comments  路  Source: nestjs/nest

With application container access, you can build modules that have annotations...

I am building a NestJS module that works with Kue, so in the application controllers I can have something like this to define a Task:

@Task({ name: 'justOneTest' })
justOneTest(done, task) {
    console.log('Received task:', task);
    setTimeout(done, 5000);
}

Then create a new job with:

kueService.create(this.justOneTest);
type question 馃檶

Most helpful comment

I have shared the code on https://github.com/erickponce/nestjs-kue feel free to use it.

Note that this is not tested on production environments, and there are improvements to be made =)

All 18 comments

Hi @erickponce,
What the @Task decorator is supposed to do?

Hi @kamilmysliwiec thank you for your response,

The @Task decorator is the way to define one task code.

So rigth now I have the module contructor using a metadata explorer to search for tasks in the controllers, then I register that task in Kue (https://github.com/Automattic/kue):

for (const { task, metadata } of this.metadataExplorer.explore(instance)) {
    KueService.registerTask(task, metadata, instance);
}

I'm just using the container to get the application modules (don't know if there is a better way to do this)

const modules = container.getModules();
modules.forEach(({ routes, components }) => {
    this.bindTasks(routes);
    this.bindTasks(components);
});

To demonstrate better,

@Task({
    name: 'justOneTest',
    concurrency: 3,
    queue: 'test',
    attempts: 3,
    ttl: 3000,
    backoff: { delay: 5 * 1000, type: 'fixed' }
})
async justOneTest(job: Job, done: DoneCallback) {
    done(null, 'Task completed!');
}

To fire that task you can do:

@Get('task')
createJob(@Res() res) {
    const job = this.kueService.createJob(this.justOneTest, { a: 'b' }).save();
    job.on('complete', (result) => res.send(result));
    job.on('failed', (err) => res.status(HttpStatus.INTERNAL_SERVER_ERROR).send(err));
}

Observation about Kue: It is a priority job queue backed by redis, built for node.js.

So just to summarize, the only thing which you wanna achieve is to create tasks in more declarative-way - using decorators with "task metadata", right?

Yes that's correct.

I'm thinking about the right solution now. The most plausible scenario is that I'll provide a function to grab a NestContainer from the instance for the more advanced users.

Yes, I like the approach of provide a function to grab the NestContainer.

+1

Soo I was thinking about this change and I realized that it's needless in fact. Since we have an access to the ModuleRef, it's possible to do the exact same thing as with the NestContainer. Take a look how this has been achieved inside CQRS module - https://github.com/nestjs/cqrs 馃檪

I changed the implementation to use ModuleRef accordingly to CQRS module and it works perfectly.

Thank you @kamilmysliwiec :slightly_smiling_face:

Awesome 馃檪

That will be awesome when you can make tasks to run later on your application like send emails , delete old users , clearing cache .. etc

Hi @erickponce, you can release the code of your module? it would be very useful.

I have shared the code on https://github.com/erickponce/nestjs-kue feel free to use it.

Note that this is not tested on production environments, and there are improvements to be made =)

@erickponce thx for share.

How can I access container here?

export function f(){
    // How to access container here?
}

@Diluka you can't access the NestContainer in a function outside the module scope of Nest.
But if you just wanted a FactoryProvider to have access to the NestContainer while still be able to use it like a function, you'd do something like:

export const FUNC = 'func';
export type Func = (...args: any[]) => any;

@Module({
  providers: [
    {
      provide: FUNC,
      useFactory(container: NestContainer) {
        return (...args: any[]) => {
          // container is available in this scope
        };
      },
      inject: [NestContainer],
    },
    AppService,
  ],
})
export class AppModule {}

@Injectable()
export class AppService {
  constructor(
    @Inject(FUNC) f: Func,
  ) {
    f(/*args here*/);
  }
}

@marcus-sa I just want to get TypeORM stuff out of IoC. And I find a way.

// main.ts
useContainer(app);

// func.ts
getFromContainer(EntityManager);

both useContainer and getFromContainer are imported from typeorm

My goal is to create a simple helper function to get metadata from an entity. I want to keep this function simple ---- import and use

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yanshuf0 picture yanshuf0  路  3Comments

menme95 picture menme95  路  3Comments

marshall007 picture marshall007  路  3Comments

rafal-rudnicki picture rafal-rudnicki  路  3Comments

anyx picture anyx  路  3Comments