Files
What you'll learn
- Write an async factory provider and explain what Nest waits for before it constructs dependents and listens
- Chain async providers through inject so one waits for another
- Tell startup-time work, which belongs in an async provider, from per-request work, which does not
Asynchronous Providers
Some things an application needs are not ready when it starts. A database connection has to be opened, a configuration file read, a remote service asked for its schema. If handlers can run before those have finished, the first requests fail in ways that never happen again, which is the worst kind of bug to reproduce. Nest's answer is that a provider may be asynchronous: its factory returns a Promise, Nest waits for it, and nothing that depends on the provider is constructed, and the application does not listen, until the Promise has resolved.
An async factory
The syntax is the factory provider from the last lesson with async in front:
{
provide: 'ASYNC_CONNECTION',
useFactory: async () => {
const connection = await createConnection(options);
return connection;
},
}
That is the whole feature. The factory can await anything. What it resolves to is the provider, and @Inject('ASYNC_CONNECTION') receives the resolved value, never the Promise. Classes that inject it are constructed only after it resolved, so their constructors can use it at once.
Waiting is transitive
Async providers compose through inject. A factory that needs a resolved async provider lists its token, and Nest resolves the chain in order:
{
provide: 'SCHEMA',
useFactory: async (connection: Connection) => connection.loadSchema(),
inject: ['ASYNC_CONNECTION'],
},
{
provide: Repository,
useFactory: (connection: Connection, schema: Schema) => new Repository(connection, schema),
inject: ['ASYNC_CONNECTION', 'SCHEMA'],
},
SCHEMA waits for the connection; Repository waits for both. Independent async providers resolve concurrently, so two slow factories do not add up. The console shows the effect: Nest application successfully started prints only after the last one resolved, and the boot time it reports includes the waiting.
What it is for, and what it is not
An async provider runs its factory once, when the module initialises. It is the place for work that must happen before the first request and never again: connecting, loading, warming a cache. It is not the place for anything per request, which belongs in a handler or a request-scoped provider, and not for work that can fail and should be retried while the application keeps serving. Lesson 9 shows onModuleInit, a lifecycle hook that can do the same waiting inside a class, which is the better fit when the class already exists as a provider and only needs a moment to get ready.
Your task
The cats now sit behind a Connection that has to be opened, and the shelter starts with cats that loadSeed() delivers late. Both are asynchronous, and the starter treats neither as such.
- The
CONNECTIONfactory hands the connection over unopened, so every request answers503. Open it in the factory, and make Nest wait for that before anything uses it. - Provide the seed under
SEED_CATS, loaded withloadSeed(), and give it to the repository's factory so the shelter starts with those cats.
Watch the console: the connection's own line prints before Nest reports the application started, because the start waited for it.
When it fails
- Every request answers
503 the connection to sqlite://cats is not open: the factory returned the connection without awaitingopen(), or awaited it withoutasync, which TypeScript rejects. GET /catslists no cats at first: the repository was built with an empty seed. The factory needs the seed's token ininject, and the seed itself needs a provider.Nest can't resolve dependencies of the CatsRepository (CONNECTION, Symbol(CATS_CONFIG), ?): the factory listsSEED_CATSininjectbut nothing provides it yet.- The repository receives a Promise: the seed factory is not
async, so it returnedloadSeed()'s Promise as a plain value and Nest did not wait. Only a factory markedasync(or returning a Promise Nest can see) is awaited.
Remember
- An
asyncfactory's resolved value is the provider; Nest waits before constructing anything that depends on it, and before listening. - Async providers chain through
inject, and independent ones resolve concurrently. - One run at startup: connect, load, warm. Not per request.
Stuck? Show a hint
Make the CONNECTION factory async: create the connection, await its open(), return it. Add a provider for 'SEED_CATS' whose async factory returns loadSeed(). Then add 'SEED_CATS' to the repository factory's inject list and pass the third argument on to the constructor.
Press Run tests to start the app. Its log appears here.Graded endpoints
Nest waited for the async factory: the connection is open, and the seed's two cats are counted
The seed arrived, late but before the repository was built
Ids continue after the seed
Capacity still comes from the config