0Pricing
NestJS Enterprise Backend APIs · Aula

Módulos carregados sob demanda e alternâncias de recursos

Carregue módulos opcionais sob demanda com LazyModuleLoader para reduzir o custo de inicialização.

Módulos carregados sob demanda e alternâncias de recursos é uma aula grátis de NestJS Enterprise Backend APIs no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de NestJS Enterprise Backend APIs, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de NestJS Enterprise Backend APIs inclui 4 aulas no total.

Partes desta aula ainda não foram traduzidas e aparecem em inglês.

Why Eager Loading Hurts Startup

By default, NestJS instantiates every module in your imports graph at bootstrap. For an enterprise API with dozens of optional features — a PDF exporter, a payment gateway, an AI scoring engine — that means paying the full provider-instantiation and connection-warmup cost before the app even accepts a request.

  • Heavy SDKs (Stripe, AWS, gRPC clients) run their constructors eagerly.
  • Modules a given deployment never uses still load.
  • Cold-start latency grows linearly with the module graph.

The fix: load select feature modules lazily, only when first invoked.

The LazyModuleLoader

Nest ships a built-in LazyModuleLoader (from @nestjs/core). You inject it like any provider, then call load() with a factory that returns the module. Nest registers the module's providers on demand and caches the resulting reference for subsequent calls.

Key traits:

  • Lazy modules are not listed in any imports array.
  • They do not register controllers, resolvers, or enhancers — only providers.
  • The first load() instantiates; later calls return the cached ModuleRef.
import { Injectable } from '@nestjs/common';
import { LazyModuleLoader } from '@nestjs/core';

@Injectable()
export class ReportsService {
  constructor(private readonly lazyModuleLoader: LazyModuleLoader) {}

  async generate(): Promise<void> {
    const { PdfModule } = await import('./pdf/pdf.module');
    const moduleRef = await this.lazyModuleLoader.load(() => PdfModule);
    // moduleRef now exposes PdfModule's providers
  }
}

Resolving a Provider From the Lazy Module

The object returned by load() is a ModuleRef. Use its get() method to pull a concrete provider out of the freshly-loaded module. Because lazy modules are isolated, request a provider only after the module is loaded.

For request-scoped or transient providers use moduleRef.resolve() instead of get().

async generate(): Promise<Buffer> {
  const { PdfModule } = await import('./pdf/pdf.module');
  const moduleRef = await this.lazyModuleLoader.load(() => PdfModule);

  const pdfService = moduleRef.get(PdfService);
  return pdfService.render({ title: 'Invoice' });
}

Dynamic import() Is What Saves the Bytes

The real startup win comes from pairing LazyModuleLoader.load() with a dynamic import(). A static top-level import pulls the module — and its heavy transitive deps — into the bootstrap bundle. A dynamic import() defers that file evaluation until the call runs.

  • Static import { PdfModule } at the top of the file = loaded at startup.
  • await import('./pdf/pdf.module') inside the method = loaded on first use.

So always import the lazy module's file dynamically, never statically.

A Lazy Feature Module Definition

The lazy module itself is an ordinary @Module — there is nothing special in its decorator. What makes it lazy is purely how it is consumed (via LazyModuleLoader rather than an imports array).

Keep its providers self-contained so loading it does not drag in the whole app.

import { Module } from '@nestjs/common';
import { PdfService } from './pdf.service';

@Module({
  providers: [PdfService],
  exports: [PdfService],
})
export class PdfModule {}

Caching Makes Repeated Loads Cheap

Nest internally keeps a registry of already-loaded lazy modules keyed by the factory result. So calling load() repeatedly with the same module class is effectively free after the first hit — no double instantiation, no duplicate connections.

This means you can safely call load() inline in a hot handler without guarding it yourself; the framework deduplicates. The one-time cost is paid on the first request that needs the feature.

Feature Toggles: Gate the Load

Feature toggles and lazy loading are a natural pair. Instead of conditionally registering modules at compile time, you check a flag at runtime and only load() the module when the flag is on. A disabled feature then costs nothing — not even its constructor.

  • Flag from env, config service, or a remote flag provider (LaunchDarkly, Unleash).
  • If the toggle is off, short-circuit before importing.
@Injectable()
export class ExportService {
  constructor(
    private readonly lazyModuleLoader: LazyModuleLoader,
    private readonly flags: FeatureFlagService,
  ) {}

  async export(payload: ExportDto) {
    if (!this.flags.isEnabled('pdf-export')) {
      throw new ForbiddenException('Feature disabled');
    }
    const { PdfModule } = await import('./pdf/pdf.module');
    const ref = await this.lazyModuleLoader.load(() => PdfModule);
    return ref.get(PdfService).render(payload);
  }
}

A Minimal Flag Service (Standalone)

A feature-flag check is just a deterministic lookup. Here is a framework-free version you can reason about and test in isolation — the same logic a Nest FeatureFlagService would wrap. It reads a flag map and falls back to a default when the key is unknown.

class FeatureFlags {
  constructor(private readonly flags: Record<string, boolean>) {}

  isEnabled(key: string, fallback = false): boolean {
    return this.flags[key] ?? fallback;
  }
}

const flags = new FeatureFlags({ 'pdf-export': true, 'ai-scoring': false });

console.log(flags.isEnabled('pdf-export'));   // true
console.log(flags.isEnabled('ai-scoring'));   // false
console.log(flags.isEnabled('unknown', true)); // true (fallback)

Controllers and Enhancers Are Ignored

A critical limitation: when a module is loaded lazily, Nest registers its providers only. It deliberately skips:

  • controllers — no new HTTP routes appear.
  • Global guards, interceptors, pipes, filters declared in the module.
  • GraphQL resolvers.

So a lazy module cannot add endpoints. Expose the feature through a controller in an eagerly-loaded module that delegates to the lazily-loaded provider.

@Controller('reports')
export class ReportsController {
  constructor(private readonly reports: ReportsService) {}

  @Post('pdf')
  async pdf(@Body() dto: ExportDto) {
    // controller is eager; PdfModule is loaded lazily inside the service
    return this.reports.generate(dto);
  }
}

Warming Up vs Lazy: Pick Per Feature

Lazy loading trades a one-time first-request latency spike for a faster, lighter startup. That is the right deal for rarely used, expensive features. For features on the hot path, eager loading (or an explicit warm-up on onApplicationBootstrap) avoids penalizing the first user.

Decision guide:

  • Lazy: heavy SDK, used by <X% of requests, optional per deployment.
  • Eager: core domain, every request, latency-sensitive.
  • Lazy + warm-up: heavy but predictably needed soon after boot.
@Injectable()
export class Warmer implements OnApplicationBootstrap {
  constructor(private readonly lazyModuleLoader: LazyModuleLoader) {}

  async onApplicationBootstrap() {
    if (process.env.PRELOAD_PDF === 'true') {
      const { PdfModule } = await import('./pdf/pdf.module');
      await this.lazyModuleLoader.load(() => PdfModule); // pay cost now, off the request path
    }
  }
}

Measuring the Win

Quantify before and after. Wrap bootstrap timing and the first lazy load() to confirm the trade-off is real for your workload.

  • Startup time should drop by the cumulative constructor + connection cost of the deferred modules.
  • First-call latency for the lazy feature absorbs that cost once.
  • Watch P99 of the first request after deploy — that is where the deferred cost surfaces.

If the lazy feature is hit on nearly every request, the numbers will tell you to switch it back to eager.

const t0 = performance.now();
const { PdfModule } = await import('./pdf/pdf.module');
const ref = await this.lazyModuleLoader.load(() => PdfModule);
this.logger.log(`Lazy PdfModule ready in ${Math.round(performance.now() - t0)}ms`);

Quick Check

You lazily load PdfModule via LazyModuleLoader. PdfModule declares a controller with a @Post('pdf') route. After loading, the route returns 404. What is the correct explanation and fix?

Recap

You learned how to shrink NestJS startup cost with on-demand modules:

  • LazyModuleLoader.load(() => SomeModule) instantiates a module's providers on first use and caches the result.
  • Pair it with a dynamic import() so the module's file (and heavy deps) is never evaluated at bootstrap.
  • Resolve providers via moduleRef.get() (or resolve() for scoped providers).
  • Feature toggles gate the load: a disabled feature costs nothing, not even a constructor.
  • Lazy modules register no controllers, resolvers, or enhancers — delegate from an eager controller.
  • Choose lazy for heavy, rarely-used, optional features; eager (or lazy + warm-up) for hot-path code. Measure startup and first-call latency to confirm the trade-off.

Perguntas Frequentes

A aula “Módulos carregados sob demanda e alternâncias de recursos” é grátis?

Sim — o texto completo de “Módulos carregados sob demanda e alternâncias de recursos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de NestJS Enterprise Backend APIs, atualize para CoddyKit PRO. O curso de NestJS Enterprise Backend APIs inclui 4 aulas no total.

O que vou aprender em “Módulos carregados sob demanda e alternâncias de recursos”?

Carregue módulos opcionais sob demanda com LazyModuleLoader para reduzir o custo de inicialização. Você pratica NestJS Enterprise Backend APIs com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar NestJS Enterprise Backend APIs?

Nenhuma experiência prévia é necessária. NestJS Enterprise Backend APIs no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Módulos carregados sob demanda e alternâncias de recursos”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de NestJS Enterprise Backend APIs?

Sim. Cada aula de NestJS Enterprise Backend APIs inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Portas e adaptadores para isolamento do domínio
  2. Registro dinâmico de provedores com DiscoveryService
  3. Módulos carregados sob demanda e alternâncias de recursos
  4. Pontos de extensão com a API de referência de módulos
← Voltar para NestJS Enterprise Backend APIs