nest icon indicating copy to clipboard operation
nest copied to clipboard

Feat: onApplicationReady Hook

Open StimulCross opened this issue 1 year ago • 14 comments
trafficstars

Is there an existing issue that is already proposing this?

  • [X] I have searched the existing issues

Is your feature request related to a problem? Please describe it

There are a lot of potential tasks that can be done successfully only after the start of an application (when the app is ready to handle connections).

The onApplicationBootstrap triggers before await app.listen(), so it can't be used for such tasks (correct me if I'm wrong).

Describe the solution you'd like

Although this can be easily implemented using events, another good option would be introducing a new hook that triggers after await app.listen().

Teachability, documentation, adoption, migration strategy

The hook's name can be onApplicationReady or onApplicationStart. The usage of the hook will be pretty similar to the other hooks:

export class CustomProvider implements OnApplicationReady {
    onApplicationReady(): void {
        console.log('Ready!')
    }
}

What is the motivation / use case for changing the behavior?

One of the use cases could be webhook subscriptions. When I subscribe to webhooks, the publisher sends me a POST request to verify my webhook callback. It is safer to perform this task after the application is ready to listen for connections.

StimulCross avatar Jul 20 '24 18:07 StimulCross

+1 support Is this feature being considered by the NestJs team?

CarltonK avatar Sep 03 '24 16:09 CarltonK

I'm not sure about the naming and the role of that hook. I'd need to think more about it. But I can say that maybe we need a more specific name because 'ready' can be interpreted in several ways

You can supply a callback already to app.listen method but I get that you want to make things more coupled with nestjs providers and modules; more high-level

micalevisk avatar Sep 03 '24 16:09 micalevisk

I'm not sure about the naming and the role of that hook. I'd need to think more about it. But I can say that maybe we need a more specific name because 'ready' can be interpreted in several ways

You can supply a callback already to app.listen method but I get that you want to make things more coupled with nestjs providers and modules; more high-level

@micalevisk Maybe something like onApplicationInit

CarltonK avatar Sep 03 '24 16:09 CarltonK

'init' would be confusing because we have app.init()

micalevisk avatar Sep 03 '24 16:09 micalevisk

What about using https://docs.nestjs.com/techniques/events and then

await app.listen(...);
await app.get(EventEmitter2).emit(...);

Done(?) This gives you full control too

kamilmysliwiec avatar Sep 17 '24 09:09 kamilmysliwiec

@kamilmysliwiec I personally already use this approach. It just feels like this functionality should be available out of the box with the rest of the hooks and have similar usage. Without this hook, lifecycle hooks feel incomplete.

StimulCross avatar Sep 17 '24 10:09 StimulCross

The thing is, onApplicationReady is just somewhat vague as "ready" might mean different things depending on the context. For some, it's just that the app is already listening to HTTP requests, but for those who have hybrid applications (HTTP + message broker) the definition is probably going to be different. Not to mention that those who use the "Standalone application" mode wouldn't use that hook at all.

kamilmysliwiec avatar Sep 17 '24 10:09 kamilmysliwiec

Yeah, I didn't take those things into account when I came up with the hook name. Naming is not my strong point :) There's a lot to think about here. You guys know better anyway.

StimulCross avatar Sep 17 '24 10:09 StimulCross

I'm also interested by this feature. I have been using NestJS as my backend framework for years and I'd love to contribute to NestJS.

The thing is, onApplicationReady is just somewhat vague as "ready" might mean different things depending on the context. For some, it's just that the app is already listening to HTTP requests, but for those who have hybrid applications (HTTP + message broker) the definition is probably going to be different. Not to mention that those who use the "Standalone application" mode wouldn't use that hook at all.

@kamilmysliwiec I think it would make sense to name the lifecycle "events" related to the methods we use to bootstrap a NestJS app:

const app = await NestFactory.create(AppModule);
// Triggers the method `onApplicationCreated` of all `OnApplicationCreated` implementations
await app.listen(...);
// Triggers the method `onApplicationListening` of all `OnApplicationListening` implementations.

What do you think of these namings?

SylvainMarty avatar Sep 28 '24 12:09 SylvainMarty

On another subject, I'd like to react to this comment:

What about using https://docs.nestjs.com/techniques/events and then

await app.listen(...);
await app.get(EventEmitter2).emit(...);

Done(?) This gives you full control too

I am not a big fan of EventEmitter2 but I have been implementing in by own synchronous event bus (with handlers priority management) in my backends for some time and it would really be awesome if NestJS had an internal event bus we could subscribe to when we need it. Having both onApplication[...] hooks and an event bus would give us much more freedom in how we want to architect our application code. I would also love to provide a PR with an implementation of it in NestJS so we can discuss about it together if you're interested.

SylvainMarty avatar Sep 28 '24 12:09 SylvainMarty

@SylvainMarty as we can do app.init() without listen, the 'on application created' name would be confusing

I still have no ideia about the naming of this

micalevisk avatar Sep 28 '24 14:09 micalevisk

@micalevisk Thank you for your answer.

I'm not familiar with the await app.init() method but from its documentation, it's an optional method that calls the Nest lifecycle events. From the source code, calling await app.listen() actually calls await app.init() in the background.

To me, it would be useful to have the following lifecycle events:

  1. await NestFactory.create(AppModule) -> OnApplicationCreate hooks are called
  2. await app.init() -> OnApplicationInit hooks are called (previously called OnApplicationBootstrap, we could keep the old hook to avoid introducing breaking change)
  3. If await app.listent() is called -> OnApplicationListen hooks are called
  4. Using the listen method or not, the more general purpose hooks OnApplicationStart are called

About the method await app.startAllMicroservices(): since it calls await mc.listen() of the connected microservice, the lifecycle events would follow the said lifecycle for each microservice.

Let me know what you think of this.

SylvainMarty avatar Sep 28 '24 16:09 SylvainMarty

Looks good so far but I guess we won't rename nor change/deprecate existing hooks anytime soon

micalevisk avatar Sep 29 '24 00:09 micalevisk

I was looking into liveness/readiness indicators for Kubernetes (via Terminus). And found some events, such as the proposal here as being required to properly manage the probe states. I also develop Spring Boot apps and there are a lot of comparable patterns provided by that framework.

Specifically re: application events, Spring provides a set of application events that include what is being requested here, so that may be useful to compare the definitions they use.

A direct use-case for this ApplicationReadyEvent (Spring's equivalent) would allow for the readiness indicator to be determined via same logic as Spring, which is something I would like to make happen in my apps. Manually updating the readiness state after await app.listen(...) (as others have shared) is how I have approached it up to this point.

Ultimately the number of Nest's lifecycle events are somewhat finite, but I wonder if producing them via an emitter/filterable listeners as opposed to distinct hook methods could be easier/more scalable?

Some related links re: Kubernetes probes:

  • https://docs.spring.io/spring-boot/reference/features/spring-application.html#features.spring-application.application-availability
  • https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.kubernetes-probes

mckramer avatar Oct 17 '24 01:10 mckramer

Let's track this here https://github.com/nestjs/nest/pull/14143

kamilmysliwiec avatar Nov 15 '24 10:11 kamilmysliwiec