0
0
NestJSframework~15 mins

Injectable decorator in NestJS - Deep Dive

Choose your learning style9 modes available
Overview - Injectable decorator
What is it?
The Injectable decorator in NestJS marks a class as a provider that can be managed by Nest's dependency injection system. It tells Nest that this class can be created and shared automatically wherever needed. This helps organize code by letting Nest handle creating and linking objects behind the scenes. Without it, you would have to manually create and connect every class instance.
Why it matters
Without the Injectable decorator, developers would need to manually create and manage dependencies, leading to more complex, error-prone code. The decorator enables automatic and consistent object creation and sharing, making applications easier to build, test, and maintain. It solves the problem of tightly coupled code by promoting loose coupling and clear dependency management.
Where it fits
Before learning Injectable, you should understand basic TypeScript classes and decorators. After this, you can learn about NestJS modules, providers, and dependency injection patterns. Later topics include scopes, lifecycle hooks, and custom providers that build on Injectable.
Mental Model
Core Idea
The Injectable decorator marks a class so NestJS can create and share its instances automatically wherever needed.
Think of it like...
It's like putting a name tag on a tool in a shared toolbox so anyone can find and use it without asking who owns it or how to make it.
┌───────────────┐
│  @Injectable  │
└──────┬────────┘
       │
       ▼
┌───────────────┐       ┌───────────────┐
│   MyService   │──────▶│  Instance A   │
└───────────────┘       └───────────────┘
       │
       ▼
┌───────────────┐       ┌───────────────┐
│  MyComponent  │──────▶│  Uses Instance │
└───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationWhat is a decorator in TypeScript
🤔
Concept: Decorators are special functions that can modify classes or their members.
In TypeScript, a decorator is a function that you place before a class or method with an @ symbol. It can add extra behavior or metadata to that class or method. For example, @Component or @Injectable are decorators that tell frameworks how to treat the class.
Result
You learn how to mark classes with extra information that frameworks can use.
Understanding decorators is key because Injectable itself is a decorator that changes how NestJS treats a class.
2
FoundationBasic class and instance creation
🤔
Concept: Classes define blueprints for objects; instances are created from classes.
A class is like a recipe, and an instance is the cake made from that recipe. Normally, you create instances by calling new MyClass(). Without decorators, you manage these instances yourself.
Result
You know how classes and objects work before NestJS automates this.
Knowing manual instance creation helps you appreciate how Injectable automates this process.
3
IntermediateInjectable decorator marks providers
🤔Before reading on: do you think @Injectable creates instances automatically or just marks classes? Commit to your answer.
Concept: The Injectable decorator marks a class so NestJS knows it can create and inject instances of it.
When you add @Injectable() above a class, NestJS registers it as a provider. This means Nest can create one instance and share it wherever needed. You don't call new yourself; Nest does it for you.
Result
Classes with @Injectable become managed by Nest's dependency injection system.
Understanding that Injectable signals Nest to manage creation and sharing is crucial for using dependency injection effectively.
4
IntermediateHow NestJS injects dependencies automatically
🤔Before reading on: do you think NestJS creates new instances every time or reuses one? Commit to your answer.
Concept: NestJS uses Injectable classes to automatically provide dependencies to constructors of other classes.
If a class constructor asks for another Injectable class, NestJS sees this and provides the same instance automatically. This is called dependency injection. It means you don't have to manually pass dependencies around.
Result
Dependencies are automatically created and shared, simplifying code.
Knowing that Nest reuses instances by default helps you write efficient and consistent services.
5
IntermediateScopes and lifecycle of Injectable providers
🤔Before reading on: do you think Injectable providers are always singletons? Commit to your answer.
Concept: Injectable providers can have different lifecycles or scopes, like singleton or request-scoped.
By default, Injectable providers are singletons, meaning one instance per application. But you can change scope to create new instances per request or transiently. This controls how long instances live and when they are recreated.
Result
You can control instance sharing and lifecycle for different use cases.
Understanding scopes prevents bugs related to shared state or unexpected instance reuse.
6
AdvancedCustom providers and useClass with Injectable
🤔Before reading on: can you replace an Injectable class with a different implementation at runtime? Commit to your answer.
Concept: You can customize how Injectable providers are created by using custom providers and the useClass option.
NestJS allows you to tell the injector to use a different class or factory instead of the default Injectable class. This helps with testing, swapping implementations, or adding extra logic during creation.
Result
You gain flexibility to change provider behavior without changing dependent code.
Knowing how to customize providers unlocks advanced patterns like mocking and dynamic behavior.
7
ExpertHow Injectable works internally in NestJS
🤔Before reading on: do you think Injectable modifies the class code or just metadata? Commit to your answer.
Concept: Injectable adds metadata that NestJS reads at runtime to manage dependency injection without changing class code.
The Injectable decorator attaches metadata to the class prototype using Reflect Metadata API. NestJS reads this metadata during application startup to build a dependency graph. It then creates instances in the right order and injects them where needed. The class code itself stays unchanged.
Result
You understand the invisible wiring that makes dependency injection work.
Understanding metadata-based wiring explains why Injectable is lightweight and flexible.
Under the Hood
The Injectable decorator uses TypeScript's Reflect Metadata API to attach metadata to the class. NestJS reads this metadata to know which classes are providers. During application bootstrap, Nest builds a dependency graph from constructor parameters of these classes. It then instantiates classes in dependency order, caching instances by default (singleton scope). When a class needs a dependency, Nest injects the cached instance or creates a new one based on scope. This process happens without modifying the original class code, relying on metadata and runtime reflection.
Why designed this way?
NestJS uses metadata and decorators to keep class code clean and separate concerns. This design allows developers to write plain classes without boilerplate. Using metadata avoids runtime code changes, improving performance and compatibility. Alternatives like manual wiring or code generation are more error-prone or complex. The decorator pattern fits well with TypeScript and modern JavaScript, making the system intuitive and extensible.
┌───────────────┐
│ @Injectable() │
└──────┬────────┘
       │
       ▼
┌───────────────────────────────┐
│  Metadata attached to class    │
│  (Reflect.defineMetadata)       │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ NestJS reads metadata at start │
│ Builds dependency graph        │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ Creates instances in order     │
│ Caches singletons             │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ Injects instances into classes │
│ via constructor parameters    │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does @Injectable automatically create a new instance every time it's injected? Commit to yes or no.
Common Belief:Many think @Injectable creates a new instance each time it is injected.
Tap to reveal reality
Reality:By default, @Injectable providers are singletons, so NestJS creates one instance and shares it everywhere.
Why it matters:Assuming new instances are created can lead to unexpected bugs with shared state or performance issues.
Quick: Does @Injectable change the class code or just add metadata? Commit to your answer.
Common Belief:Some believe @Injectable modifies the class code to add injection logic.
Tap to reveal reality
Reality:Injectable only adds metadata; the class code remains unchanged and plain.
Why it matters:Misunderstanding this can cause confusion about how injection works and debugging difficulties.
Quick: Can you inject a class without @Injectable? Commit yes or no.
Common Belief:People often think any class can be injected without @Injectable.
Tap to reveal reality
Reality:NestJS requires @Injectable to register a class as a provider for injection.
Why it matters:Missing @Injectable causes runtime errors because Nest doesn't know to create or inject the class.
Quick: Is @Injectable only for services? Commit yes or no.
Common Belief:Many believe @Injectable is only for service classes.
Tap to reveal reality
Reality:Any class can be @Injectable, including repositories, helpers, or even controllers if needed.
Why it matters:Limiting @Injectable to services restricts design flexibility and reuse.
Expert Zone
1
Injectable metadata can be extended with custom decorators to add extra behavior without losing injection capabilities.
2
The order of provider registration affects dependency resolution and can cause circular dependency issues if not managed carefully.
3
Using transient scope with Injectable creates new instances every injection, which can impact performance and state management subtly.
When NOT to use
Avoid using @Injectable for classes that do not need to be shared or injected, such as simple data models or utility functions. For those, plain classes or static methods are better. Also, for very dynamic or runtime-generated dependencies, consider using factory providers or manual injection instead.
Production Patterns
In production, @Injectable is used extensively for services, repositories, and helpers. Patterns include using scoped providers for request-specific data, custom providers for mocking in tests, and combining Injectable with lifecycle hooks for resource management. Dependency injection with Injectable enables modular, testable, and maintainable codebases.
Connections
Dependency Injection (general software pattern)
Injectable is a concrete implementation of the dependency injection pattern.
Understanding Injectable helps grasp how dependency injection decouples code and manages object lifecycles in many frameworks.
Annotations in Java
Injectable is similar to Java annotations like @Component or @Service that mark classes for injection.
Knowing Injectable clarifies how different languages use metadata to enable dependency injection.
Supply Chain Management
Injectable's role in providing dependencies is like suppliers delivering parts to factories just-in-time.
Seeing Injectable as a supplier system helps understand how dependencies are managed and delivered efficiently.
Common Pitfalls
#1Forgetting to add @Injectable on a provider class.
Wrong approach:export class MyService { getData() { return 'data'; } }
Correct approach:@Injectable() export class MyService { getData() { return 'data'; } }
Root cause:Not realizing NestJS requires @Injectable to register classes for injection.
#2Assuming each injection creates a new instance.
Wrong approach:@Injectable() export class MyService {} // Injected multiple times expecting different instances
Correct approach:@Injectable({ scope: Scope.TRANSIENT }) export class MyService {}
Root cause:Not understanding default singleton scope of Injectable providers.
#3Trying to inject a class without registering it in a module.
Wrong approach:@Injectable() export class MyService {} // Not added to module providers array
Correct approach:@Injectable() export class MyService {} @Module({ providers: [MyService] }) export class AppModule {}
Root cause:Missing provider registration in module causes injection failure.
Key Takeaways
The Injectable decorator marks classes so NestJS can create and share their instances automatically.
It uses metadata to let NestJS build a dependency graph and manage object lifecycles without changing class code.
By default, Injectable providers are singletons, but scopes can be changed to control instance creation.
Missing @Injectable or module registration causes injection errors, so both are essential.
Understanding Injectable unlocks powerful dependency injection patterns that make code modular, testable, and maintainable.