NestJS-yritysbackendien API:t · Oppitunti

Laiskasti ladattavat moduulit ja ominaisuusliput

Ladatkaa valinnaiset ominaisuusmoduulit tarpeen mukaan LazyModuleLoaderilla käynnistysajan lyhentämiseksi.

Oppitunti 3/413 vaihetta

Laiskasti ladattavat moduulit ja ominaisuusliput on ilmainen NestJS-yritysbackendien API:t-oppitunti CoddyKitissä. Tämä on oppitunti 3/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu NestJS-yritysbackendien API:t-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. NestJS-yritysbackendien API:t-kurssilla on yhteensä 4 oppituntia.

Miksi innokas lataus hidastaa käynnistystä

Oletusarvoisesti NestJS luo jokaisen imports-kaaviossa olevan moduulin käynnistyksen yhteydessä. Yritystason API:ssa, jossa on kymmeniä valinnaisia ominaisuuksia — PDF-viejä, maksupalvelu tai tekoälyyn perustuva pisteytysmoottori — tämä tarkoittaa kaikkien palveluntarjoajien luonnin ja yhteyksien lämmityksen kustannusten maksamista ennen kuin sovellus edes vastaanottaa ensimmäistä pyyntöä.

  • Raskaat SDK:t (Stripe, AWS, gRPC-asiakkaat) suorittavat konstruktorinsa innokkaasti.
  • Moduulit, joita tietty käyttöönotto ei koskaan käytä, ladataan silti.
  • Kylmäkäynnistyksen viive kasvaa lineaarisesti moduulikaavion koon mukana.

Ratkaisu on ladata valitut ominaisuusmoduulit laiskasti vasta ensimmäisen käyttökerran yhteydessä.

LazyModuleLoader

Nest sisältää valmiin LazyModuleLoader-luokan (paketista @nestjs/core). Se injektoidaan kuten mikä tahansa provider, minkä jälkeen kutsutaan load()-metodia factory-funktiolla, joka palauttaa moduulin. Nest rekisteröi moduulin providerit tarvittaessa ja tallentaa muodostuneen viitteen myöhempiä kutsuja varten.

Keskeiset ominaisuudet:

  • Lazily ladattavia moduuleja ei määritetä missään imports-taulukossa.
  • Ne eivät rekisteröi controllereita, resolvereita tai enhancereita — ainoastaan providereita.
  • Ensimmäinen load()-kutsu luo instanssin; myöhemmät kutsut palauttavat välimuistissa olevan ModuleRef-viitteen.
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
  }
}

Providerin ratkaiseminen lazily ladatusta moduulista

load()-metodin palauttama objekti on ModuleRef. Sen get()-metodilla voitte noutaa konkreettisen providerin juuri ladatusta moduulista. Koska lazily ladatut moduulit ovat eristettyjä, provider tulee pyytää vasta moduulin lataamisen jälkeen.

Request-scoped- tai transient-providereita varten käyttäkää moduleRef.resolve()-metodia get()-metodin sijaan.

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' });
}

Dynaaminen import() säästää tavut

Varsinainen käynnistysajan hyöty syntyy, kun LazyModuleLoader.load() yhdistetään dynaamiseen import()-kutsuun. Tiedoston alussa oleva staattinen import tuo moduulin — ja sen raskaat transitiiviset riippuvuudet — bootstrap-bundleen. Dynaaminen import() siirtää tiedoston evaluoinnin siihen asti, kunnes kutsu suoritetaan.

  • Staattinen import { PdfModule } tiedoston alussa = ladataan käynnistyksen yhteydessä.
  • Metodin sisällä oleva await import('./pdf/pdf.module') = ladataan ensimmäisellä käyttökerralla.

Tuokaa siis lazily ladattavan moduulin tiedosto aina dynaamisesti, ei koskaan staattisesti.

Lazily ladattavan feature-moduulin määrittely

Lazily ladattava moduuli on tavallinen @Module — sen decoratorissa ei ole mitään erityistä. Lazily latautuvaksi sen tekee ainoastaan tapa, jolla sitä käytetään (eli LazyModuleLoader-luokan kautta imports-taulukon sijaan).

Pidä sen providerit itsenäisinä, jotta moduulin lataaminen ei tuo mukanaan koko sovellusta.

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

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

Välimuisti tekee toistuvista latauksista edullisia

Nest ylläpitää sisäisesti rekisteriä jo ladatuista lazily ladatuista moduuleista factory-funktion tuloksen perusteella. Siksi load()-metodin toistuva kutsuminen saman moduuliluokan kanssa on ensimmäisen latauksen jälkeen käytännössä ilmaista — instansseja ei luoda kahdesti eikä yhteyksiä muodosteta päällekkäin.

Voitte siis kutsua load()-metodia turvallisesti suoraan kuormitetussa handlerissa ilman omaa suojausta; framework huolehtii päällekkäisten latausten yhdistämisestä. Kertaluonteinen kustannus syntyy ensimmäisestä pyynnöstä, joka tarvitsee ominaisuutta.

Feature toggle: ohjaa lataamista

Feature toggle -kytkimet ja lazy loading sopivat luontevasti yhteen. Moduulien rekisteröimisen ehdollisesti käännösvaiheessa sijaan tarkistatte lipun ajonaikana ja kutsutte load()-metodia vain, kun lippu on käytössä. Käytöstä poistettu ominaisuus ei tällöin maksa mitään — edes sen konstruktoria ei suoriteta.

  • Lippu voi tulla ympäristömuuttujasta, config-palvelusta tai etäisestä lippupalvelusta (LaunchDarkly, Unleash).
  • Jos kytkin on pois käytöstä, keskeyttäkää suoritus ennen importia.
@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);
  }
}

Pienin mahdollinen lippupalvelu (itsenäinen)

Feature flag -tarkistus on vain deterministinen haku. Tässä on frameworkista riippumaton versio, jonka toimintaa voitte tarkastella ja testata erikseen — sama logiikka, jonka Nestin FeatureFlagService voisi kääriä ympärilleen. Se lukee lippukarttaa ja käyttää oletusarvoa, jos avainta ei löydy.

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)

Controllerit ja enhancerit ohitetaan

Keskeinen rajoitus on seuraava: kun moduuli ladataan lazily, Nest rekisteröi siitä ainoastaan providerit. Se ohittaa tarkoituksella seuraavat:

  • controllers — uusia HTTP-reittejä ei synny.
  • Moduulissa määritetyt globaalit guardit, interceptorit, pipet ja filterit.
  • GraphQL-resolverit.

Lazily ladattava moduuli ei siis voi lisätä endpointteja. Julkaiskaa ominaisuus eager-tilassa ladatun moduulin controllerista, joka delegoi kutsun lazily ladatulle providerille.

@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);
  }
}

Warm-up vai lazy loading: valitkaa ominaisuuskohtaisesti

Lazy loading vaihtaa ensimmäisen pyynnön kertaluonteisen viivepiikin nopeampaan ja kevyempään käynnistykseen. Se on oikea ratkaisu harvoin käytetyille, raskaille ominaisuuksille. Kuormituspolulla oleville ominaisuuksille eager loading (tai eksplisiittinen warm-up kohdassa onApplicationBootstrap) estää ensimmäisen käyttäjän hidastumisen.

Ohje päätöksentekoon:

  • Lazy: raskas SDK, käytössä alle X prosentissa pyynnöistä, käyttöönoton mukaan valinnainen.
  • Eager: ydintoimialue, jokainen pyyntö, viiveherkkä.
  • Lazy + warm-up: raskas ominaisuus, jota tarvitaan ennustettavasti pian käynnistyksen jälkeen.
@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
    }
  }
}

Hyödyn mittaaminen

Mitatkaa tilanne ennen ja jälkeen muutoksen. Mitatkaa bootstrapin kesto ja ensimmäinen lazy load()-kutsu varmistaaksenne, että kompromissi on työkuormallanne todellinen.

  • Käynnistysajan pitäisi lyhentyä viivästettyjen moduulien konstruktorien ja yhteyksien muodostamisen yhteenlasketun kustannuksen verran.
  • Lazily ladattavan ominaisuuden ensimmäinen kutsu sisältää tämän kustannuksen kerran.
  • Seuratkaa käyttöönoton jälkeisen ensimmäisen pyynnön P99-arvoa — siinä viivästetty kustannus näkyy.

Jos lazily ladattavaa ominaisuutta käytetään lähes jokaisessa pyynnössä, mittaustulokset osoittavat, että se kannattaa vaihtaa takaisin eager-lataukseen.

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`);

Pikatarkistus

Lataatte PdfModule-moduulin lazily LazyModuleLoader-luokan avulla. PdfModule määrittää controllerin, jossa on @Post('pdf')-reitti. Lataamisen jälkeen reitti palauttaa 404-virheen. Mikä on oikea selitys ja korjaus?

Yhteenveto

Opitte pienentämään NestJS:n käynnistyskustannuksia tarvittaessa ladattavilla moduuleilla:

  • LazyModuleLoader.load(() => SomeModule) luo moduulin providerit ensimmäisellä käyttökerralla ja tallentaa tuloksen välimuistiin.
  • Yhdistäkää se dynaamiseen import()-kutsuun, jotta moduulin tiedostoa (ja raskaita riippuvuuksia) ei evaluoida bootstrapin aikana.
  • Ratkaiskaa providerit moduleRef.get()-metodilla (tai scoped-providereille resolve()-metodilla).
  • Feature toggle ohjaa lataamista: käytöstä poistettu ominaisuus ei maksa mitään, ei edes konstruktorin suoritusta.
  • Lazily ladattavat moduulit eivät rekisteröi controllereita, resolvereita tai enhancereita — delegoikaa kutsu eager-tilassa ladatusta controllerista.
  • Valitkaa lazy raskaille, harvoin käytetyille ja valinnaisille ominaisuuksille; käyttäkää eager-latausta (tai lazy + warm-up -ratkaisua) kuormituspolun koodille. Mitatkaa käynnistys- ja ensimmäisen kutsun viive varmistaaksenne ratkaisun toimivuuden.
Aloita maksutta

Opi TypeScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
20
Oppitunnit
76

Usein kysytyt kysymykset

Onko oppitunti ”Laiskasti ladattavat moduulit ja ominaisuusliput” ilmainen?

Kyllä – oppitunnin ”Laiskasti ladattavat moduulit ja ominaisuusliput” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko NestJS-yritysbackendien API:t-kurssin, päivitä CoddyKit PROhon. NestJS-yritysbackendien API:t-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Laiskasti ladattavat moduulit ja ominaisuusliput”?

Ladatkaa valinnaiset ominaisuusmoduulit tarpeen mukaan LazyModuleLoaderilla käynnistysajan lyhentämiseksi. Harjoittelet NestJS-yritysbackendien API:t-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni NestJS-yritysbackendien API:t-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin NestJS-yritysbackendien API:t-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 3/4.

Kuinka kauan ”Laiskasti ladattavat moduulit ja ominaisuusliput”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä NestJS-yritysbackendien API:t-oppitunnilla?

Kyllä. Jokainen NestJS-yritysbackendien API:t-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Portit ja adapterit toimialueen eristämiseen
  2. Palveluntarjoajien dynaaminen rekisteröinti DiscoveryServicella
  3. Laiskasti ladattavat moduulit ja ominaisuusliput
  4. Laajennuspisteet Module Reference APIlla
← Takaisin: NestJS-yritysbackendien API:t