NestJS Module 3
Providers and dependency injection interview questions
Interview questions on providers and dependency injection: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What is a provider in NestJS, and how does Nest decide what to pass to a provider's constructor?
-
Fresher to mid
Name the four recipes of a custom provider in NestJS, and say what each one injects.
Answered in the lesson Custom providers, tokens and factories
-
Fresher to mid
What does request scope do in NestJS, and what happens to a singleton that injects a request-scoped provider?
Answered in the lesson Injection scopes: singleton, request and transient
-
Mid to senior
What is a durable provider in NestJS, and what does it need besides durable true?
Answered in the lesson Durable providers for multi-tenant Nest apps
-
Fresher to mid
What does forwardRef() do in NestJS, and where does it go?
Answered in the lesson Circular dependencies, forwardRef and ESM
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
WalletService queries Postgres directly, so its unit tests need a running database and take minutes. Without changing what the service computes, how would you restructure its wiring so that tests use a fake, and how would you make sure the real database code is still tested?
- What it tests
- Using an abstract class as a port and token, useClass in the module, useValue and overrideProvider in tests, and keeping integration tests for the adapter.
- First step
- Name what the service needs from storage, as methods, and put them on an abstract class, WalletRepository. The service depends on that class only; the Postgres code moves into a subclass that the module maps to the token with useClass. A test then hands the service a fake: in this lesson's run, new WalletService(fake) returned 18000 paise without Nest at all.
- Follow-ups
-
- Why an abstract class and not an interface?
- How do you test the Postgres repository itself?
- When would you use overrideProvider() instead of a small testing module?
From the lesson Providers and constructor injection
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service opens its database connection in its constructor. After each deploy, the app logs that it started, yet the first requests fail because the connection is not ready. Explain what happens in Nest, and where you would open the connection instead.
- What it tests
- Knowing that Nest builds providers during start-up, that constructors cannot await, and the async factory and onModuleInit alternatives, with a deadline on start-up.
- First step
- Start from when the constructor runs: Nest calls it while the app starts, and a constructor returns its object at once, so the connection is still being opened when app.listen() accepts traffic. The cure is a place Nest awaits before serving: an async factory provider, which delays the classes that need the connection, or an onModuleInit() hook.
- Follow-ups
-
- Which of the two would you choose, and why?
- What happens to start-up if the database never answers?
- How would a readiness check fit in?
From the lesson Providers and constructor injection
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your service sends SMS through a gateway that must be fake in tests, a provider's sandbox in development and the live API in production, with a metrics client that only production has. How would you wire that in Nest so that no consumer knows which one it got?
- What it tests
- Choosing between useClass and useFactory for implementations, symbol tokens for an interface, optional dependencies, and keeping the choice in one place.
- First step
- Define the gateway's contract as an interface plus one exported symbol token, SMS_GATEWAY, and make every consumer inject @Inject(SMS_GATEWAY). Then decide in exactly one providers entry which implementation stands behind the token, from configuration rather than from checks spread across the code.
- Follow-ups
-
- Would you use useClass or useFactory here, and why?
- How do you handle the metrics client that only production provides?
- How does a test swap the gateway without touching the module?
From the lesson Custom providers, tokens and factories
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
After a network change, a Nest service never finishes starting: no error, no log line after the database client begins to connect, and the platform restarts it every few minutes. The client is created by an async factory provider. Explain what is happening and how you would make the failure visible.
- What it tests
- Knowing that Nest awaits async factories before building dependents, why a pending promise never fails, and how to add a deadline that names the dependency.
- First step
- Start from what Nest does with an async factory: it waits for the promise before it builds any class that needs the token. Then ask what the database client does when the network silently drops its packets, and what would turn that into an error that names the dependency.
- Follow-ups
-
- Why does the platform keep restarting it instead of reporting a failure?
- What deadline would you choose?
- Would you retry the connection during start-up?
From the lesson Custom providers, tokens and factories
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
An integration test replaces a configuration provider with overrideProvider() and a factory, and it passes, yet a colleague finds that it reads the production rate table. How would you work out why, and how would you keep tests that rely on overrides honest from now on?
- What it tests
- Debugging the wiring of a test, knowing which overrides apply to which kinds of provider, and adding assertions that fail when an override is not in effect.
- First step
- Separate two possibilities before reading any code: the override was never applied, or the code under test does not read the token that was overridden. Print, inside the test, the value the class under test actually received, and compare its token with the one passed to overrideProvider().
- Follow-ups
-
- How would you check that the token is the right one?
- What role can the Nest version play?
- What would you add to the test suite so this cannot pass silently again?
From the lesson Custom providers, tokens and factories
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A teammate made a TenantContext provider request-scoped so that services can read the tenant of each request. Afterwards latency rose, garbage collection grew, and an onModuleInit() hook in a repository stopped running. Nothing else changed. Explain all three effects, and how you would keep per-request tenant data without them.
- What it tests
- Understanding scope bubbling and what it rebuilds per request, lifecycle hooks of request-scoped classes, and request context through AsyncLocalStorage or method arguments.
- First step
- Draw the dependency chain above TenantContext: every class that injects it, directly or through another class, became request-scoped, because a singleton cannot hold a per-request dependency. Count the classes in that chain; that is how many objects every request now builds.
- Follow-ups
-
- How would you find every class the change made request-scoped?
- Why did the repository's hook stop running?
- When would you keep request scope anyway?
From the lesson Injection scopes: singleton, request and transient
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Your logger is a transient provider so that each class can label its own lines. A heap snapshot shows thousands of logger instances in an app with about 40 classes that inject it. What can cause that, and how would you find and fix the cause?
- What it tests
- Knowing that transient means one instance per consumer, how a request-scoped dependency multiplies transient instances, and how resolve() in a loop builds new instances.
- First step
- Start from the rule: a transient provider is built once for each consumer, so 40 consumers should mean about 40 loggers. Thousands mean that consumers are being built again and again, or that code builds loggers outside constructor injection. Look for both.
- Follow-ups
-
- How would you confirm which classes are being rebuilt?
- Could a single transient logger shared by everyone work instead?
- What does INQUIRER give a transient logger, and what should it not be used for?
From the lesson Injection scopes: singleton, request and transient
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to staff
Every log line of a request, in every service, must carry that request's id, including lines written after awaits and in nested calls, and no service should become request-scoped. How would you build this in Nest, and how would you prove it works with many requests in flight?
- What it tests
- Using AsyncLocalStorage with a middleware and run(), reading the store from singletons, handling code outside requests, and testing concurrency rather than a single request.
- First step
- Keep every service a singleton and move the id into a per-request store: a middleware, the first code each request meets, starts a store holding the id, and the logger reads it whenever it writes a line. Then design a test that sends overlapping requests, since one request at a time proves nothing about isolation.
- Follow-ups
-
- Where does the request id come from, and what if the client sends one?
- What does the logger do for lines written outside any request?
- How do you keep the store from growing into a bag of objects?
From the lesson Injection scopes: singleton, request and transient
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A pull request speeds up a multi-tenant Nest API by marking five request-scoped providers durable, one per tenant. Tests pass. What would you check in review before approving it, and what test would you ask for?
- What it tests
- Knowing what a durable provider may hold, how a durable class captures the data of a tenant's first request, and writing an isolation test that fails on the leak.
- First step
- Go through the five providers one value at a time and ask whether each value is the same for every request of the tenant. Settings, clients and translations are; the user, the request id, the time and anything derived from them are not, and a provider holding any of those must not be durable.
- Follow-ups
-
- What does a durable provider receive when it injects REQUEST?
- How would the test you ask for fail on a leak?
- What should happen to a durable provider that needs a per-request value?
From the lesson Durable providers for multi-tenant Nest apps
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Your Nest service keeps a durable sub-tree per tenant. Tenants grew from 200 to 20,000, and memory now climbs until the instances restart. Explain why, estimate the memory, and say what you would change.
- What it tests
- Understanding how long durable trees live, estimating memory per tree across instances, bounding the strategy's map, and choosing other ways to isolate tenants at scale.
- First step
- Start from the lifetime: a durable tree lives as long as the strategy keeps the tenant's context id, and a simple strategy keeps every id it has seen until the process stops. So memory is the trees' size times every tenant each instance has served since it started. Measure one tree, then multiply.
- Follow-ups
-
- How would you bound the memory without losing the speed-up?
- What happens after each deploy?
- At what point would you drop durable providers altogether?
From the lesson Durable providers for multi-tenant Nest apps
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A ContextIdStrategy in your team's code reads the tenant from an x-tenant-id header, while authentication uses the caller's API key. Every endpoint checks the key. Is the header still a problem, and how would you fix it and prove the fix?
- What it tests
- Recognising that a client-chosen value decides which tenant's data a request reaches, binding the tenant to the verified identity, and a regression test that fails on the weak version.
- First step
- Ask what decides which tenant's tree a request uses. Here it is the header, which the client writes, while the key only proves who the caller is. If nothing compares the two, a valid key with another tenant's id in the header reaches that tenant's providers.
- Follow-ups
-
- Where in the request should the tenant be set?
- Would checking the header against the key be enough?
- What does the regression test send?
From the lesson Durable providers for multi-tenant Nest apps
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
After moving a Nest 12 service from CommonJS to ES modules, it crashes on start with "ReferenceError: Cannot access 'WalletService' before initialization". The two services involved already use forwardRef() on both sides, and the CommonJS build worked. Explain the crash and fix it.
- What it tests
- Understanding module evaluation order in ESM, the decorator metadata that reads classes at load time, why forwardRef() cannot help, and the fixes that remove or neutralise that read.
- First step
- Read the first line of the stack trace: it names a compiled file and a line. Open that line; it is the decorator metadata of one service, which refers to the other class while the files are still loading. That read happens before Nest runs, which is why forwardRef() makes no difference.
- Follow-ups
-
- Why did the CommonJS build not crash?
- Which fix would you choose, and why?
- How would you stop the next cycle from reaching a deploy?
From the lesson Circular dependencies, forwardRef and ESM
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A CommonJS Nest app fails with UndefinedDependencyException at index [0], and the message suggests replacing import type with import. Every import in the file is a normal import. What else produces this message, and how do you find and fix it?
- What it tests
- Knowing the causes of an undefined recorded type, recognising a CommonJS import cycle behind a misleading hint, and choosing between forwardRef() and a refactor.
- First step
- Take the message literally: the recorded type of that parameter was undefined when the class was decorated. With normal imports, the class was not exported yet when this file read it, which is what a file that imports this one back produces in CommonJS.
- Follow-ups
-
- How do you find the other file of the cycle?
- Why does forwardRef() on both sides make it start?
- What would happen to the same files under ESM?
From the lesson Circular dependencies, forwardRef and ESM
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
OrdersService charges meals through WalletService, and WalletService calls OrdersService to describe each charge. The team keeps the cycle with forwardRef() and a type wrapper. Would you leave it, and if not, how would you remove it without changing behaviour?
- What it tests
- Judging the cost of a kept cycle, finding what each side needs from the other, and choosing among a third provider, an event and runtime lookup, with checks that keep cycles out.
- First step
- List what each service calls on the other. Usually one direction needs only a small piece, such as the last order of a customer. That piece is the candidate for a provider of its own that both use, which turns the cycle into a chain.
- Follow-ups
-
- When is an event better than a third provider?
- Why not use ModuleRef in WalletService instead?
- How do you keep new cycles out of the code base?
From the lesson Circular dependencies, forwardRef and ESM
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service calls moduleRef.get(TaxTable) and gets UnknownElementException, although its module imports the module that exports TaxTable, and a second call, moduleRef.get(Basket), fails too for a request-scoped Basket. Explain both failures and what you would change.
- What it tests
- Knowing the strict lookup of ModuleRef.get(), why get() refuses scoped providers, resolve() and context ids, and when to replace runtime lookup with constructor injection.
- First step
- Separate the two calls. The first is about where get() looks: by default only at the providers of the module itself. The second is about what get() returns: existing instances only, which a request-scoped provider does not have outside a request's sub-tree.
- Follow-ups
-
- When is strict false acceptable?
- How do you get the Basket of the current request?
- Why might constructor injection be the better fix for both?
From the lesson ModuleRef, lazy modules and the discovery service
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
To cut serverless cold starts, a team lazy-loads ReportsModule. Afterwards its /reports routes answer 404, a cache that its onModuleInit() hook warmed is always cold, and right after each cold start some invoice requests fail. Explain all three and propose a design.
- What it tests
- Knowing what lazy modules cannot register, that hooks do not run in them, the cost model of lazy loading, and concurrency on the first load.
- First step
- Treat the three symptoms separately and start from what a lazy module is: a module built on demand after start-up, when routes and lifecycle hooks have already been dealt with. Then look at what happens when several requests trigger the first load at the same moment.
- Follow-ups
-
- How would you keep the routes and still load the heavy part lazily?
- Where should the warm-up go?
- How do you make the first load safe under concurrency?
From the lesson ModuleRef, lazy modules and the discovery service
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
Design a way for teams to add a payment method to a Nest service by adding one class, without editing a central list, so that a duplicate or a missing method is caught before customers see it. What would you build, and what would you test?
- What it tests
- Using DiscoveryService with createDecorator(), building the registry at start-up, handling scoped handlers and duplicates, and comparing discovery with an explicit list and runtime lookup.
- First step
- Decide where the list of handlers lives. With a decorator made by DiscoveryService.createDecorator(), the list lives on the classes, and a registry can collect it once every provider exists, at start-up, which is when a duplicate or a gap should fail.
- Follow-ups
-
- What does the registry do with a request-scoped handler?
- When is an explicit list better than discovery?
- How do you test the registry?
From the lesson ModuleRef, lazy modules and the discovery service
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes