Passer au contenu

Chargement paresseux des modules

Par défaut, les modules sont chargés de manière proactive, ce qui signifie que dès que l’application se charge, tous les modules le sont également, qu’ils soient immédiatement nécessaires ou non. Bien que cela convienne à la plupart des applications, cela peut devenir un goulot d’étranglement pour les applications et les travailleurs fonctionnant dans un environnement serverless, où la latence au démarrage (“cold start”) est cruciale.

Le chargement paresseux peut aider à réduire le temps de démarrage en ne chargeant que les modules nécessaires à l’invocation spécifique de la fonction serverless. De plus, vous pourriez également charger d’autres modules de manière asynchrone une fois que la fonction serverless est “chaude” pour accélérer encore plus le temps de démarrage pour les appels suivants (enregistrement différé des modules).

Pour commencer

Pour charger des modules à la demande, Nest fournit la classe LazyModuleLoader qui peut être injectée dans une classe de manière normale :

cats.service.ts
@Injectable()
export class CatsService {
constructor(private lazyModuleLoader: LazyModuleLoader) {}
}
cats.service.ts
@Injectable()
@Dependencies(LazyModuleLoader)
export class CatsService {
constructor(lazyModuleLoader) {
this.lazyModuleLoader = lazyModuleLoader;
}
}

Alternativement, vous pouvez obtenir une référence au fournisseur LazyModuleLoader depuis votre fichier de démarrage de l’application (main.ts), comme suit :

// "app" représente une instance de l'application Nest
const lazyModuleLoader = app.get(LazyModuleLoader);

Avec cela en place, vous pouvez maintenant charger n’importe quel module en utilisant la construction suivante :

const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);

De plus, les modules chargés paresseusement partagent le même graphe de modules que ceux chargés de manière proactive au démarrage de l’application ainsi que tous les autres modules paresseux enregistrés ultérieurement dans votre application.

lazy.module.ts est un fichier TypeScript qui exporte un module Nest régulier (aucun changement supplémentaire n’est requis).

La méthode LazyModuleLoader#load renvoie la référence du module (de LazyModule) qui vous permet de naviguer dans la liste interne des fournisseurs et d’obtenir une référence à n’importe quel fournisseur en utilisant son jeton d’injection comme clé de recherche.

Par exemple, disons que nous avons un LazyModule avec la définition suivante :

@Module({
providers: [LazyService],
exports: [LazyService],
})
export class LazyModule {}

Avec cela, nous pourrions obtenir une référence au fournisseur LazyService, comme suit :

const { LazyModule } = await import('./lazy.module');
const moduleRef = await this.lazyModuleLoader.load(() => LazyModule);
const { LazyService } = await import('./lazy.service');
const lazyService = moduleRef.get(LazyService);

Chargement paresseux des contrôleurs, passerelles et résolveurs

Étant donné que les contrôleurs (ou résolveurs dans les applications GraphQL) dans Nest représentent des ensembles de routes/chemins/sujets (ou requêtes/mutations), vous ne pouvez pas les charger de manière paresseuse en utilisant la classe LazyModuleLoader.

Par exemple, disons que vous construisez une API REST (application HTTP) avec un driver Fastify en arrière-plan (utilisant le package @nestjs/platform-fastify). Fastify ne vous permet pas d’enregistrer des routes après que l’application soit prête/écoute avec succès des messages. Cela signifie même si nous avons analysé les mappages de route enregistrés dans les contrôleurs du module, toutes les routes chargées de manière paresseuse ne seraient pas accessibles puisqu’il n’y a aucun moyen de les enregistrer à l’exécution.

De même, certaines stratégies de transport que nous fournissons dans le cadre du package @nestjs/microservices (y compris Kafka, gRPC ou RabbitMQ) nécessitent de s’abonner/écouter des sujets/canaux spécifiques avant que la connexion soit établie. Une fois que votre application commence à écouter des messages, le framework ne sera pas en mesure de s’abonner/écouter de nouveaux sujets.

Enfin, le package @nestjs/graphql avec l’approche “code first” activée génère automatiquement le schéma GraphQL à la volée en fonction des métadonnées. Cela signifie qu’il nécessite que toutes les classes soient chargées au préalable. Sinon, il ne serait pas possible de créer le schéma approprié et valide.

Cas d’utilisation courants

La plupart du temps, vous verrez des modules chargés de manière paresseuse dans des situations où votre travailleur/tâche cron/fonction lambda & serverless/webhook doit déclencher différents services (logique différente) en fonction des arguments d’entrée (chemin de route/date/paramètres de requête, etc.). D’autre part, le chargement paresseux des modules peut ne pas avoir beaucoup de sens pour les applications monolithiques, où le temps de démarrage est plutôt sans importance.