Build Large-Scale Node.js Projects with LoopBack 4 Core
LoopBack 4 core provides an IoC container and DI framework in TypeScript
designed for async-first, large-scale Node.js applications. Import from
@loopback/core (not @loopback/context). This skill covers only
@loopback/core — not REST, repositories, or other LoopBack modules.
Architecture Decision Tree
- Need to manage artifacts and their dependencies? -> Context & Bindings
(context-and-bindings.md)
- Need loose coupling between artifact construction and behavior? ->
Dependency Injection
(dependency-injection.md)
- Need a pluggable system where others can add capabilities? -> Extension
Point/Extension (extension-points.md)
- Need cross-cutting concerns (caching, logging, tracing)? -> Interceptors
(interceptors-and-observers.md)
- Need to hook into app start/stop? -> Life Cycle Observers
(interceptors-and-observers.md)
- Need runtime-configurable artifacts? -> Configuration
(configuration.md)
- Need custom decorators, parameterized classes, or advanced patterns? ->
Advanced Recipes (advanced-recipes.md)
Quick Start: Minimal Extensible Application
import {
Application,
BindingKey,
Component,
Binding,
createBindingFromClass,
extensionPoint,
extensions,
BindingTemplate,
extensionFor,
injectable,
Getter,
config,
} from '@loopback/core';
// 1. Define the extension contract
export interface Greeter {
language: string;
greet(name: string): string;
}
export const GREETER_EXTENSION_POINT_NAME = 'greeters';
export const asGreeter: BindingTemplate = binding => {
extensionFor(GREETER_EXTENSION_POINT_NAME)(binding);
binding.tag({namespace: 'greeters'});
};
// 2. Define the extension point
@extensionPoint(GREETER_EXTENSION_POINT_NAME)
export class GreetingService {
constructor(
@extensions() private getGreeters: Getter<Greeter[]>,
@config() public readonly options?: {color: string},
) {}
async greet(language: string, name: string): Promise<string> {
const greeters = await this.getGreeters();
const greeter = greeters.find(g => g.language === language);
return greeter ? greeter.greet(name) : `Hello, ${name}!`;
}
}
// 3. Implement extensions
@injectable(asGreeter)
export class EnglishGreeter implements Greeter {
language = 'en';
greet(name: string) {
return `Hello, ${name}!`;
}
}
@injectable(asGreeter)
export class ChineseGreeter implements Greeter {
language = 'zh';
greet(name: string) {
return `${name},你好!`;
}
}
// 4. Bundle into a component
export const GREETING_SERVICE = BindingKey.create<GreetingService>(
'services.GreetingService',
);
export class GreetingComponent implements Component {
bindings: Binding[] = [
createBindingFromClass(GreetingService, {key: GREETING_SERVICE}),
createBindingFromClass(EnglishGreeter),
createBindingFromClass(ChineseGreeter),
];
}
// 5. Compose components via nesting
export class CoreComponent implements Component {
// services: auto-registered service/provider classes
services = [SomeUtilityService];
}
export class AppComponent implements Component {
// components: nested components (registered recursively)
components = [CoreComponent, GreetingComponent];
// services: additional services for this layer
services = [AppSpecificService];
}
// 6. Wire up the application
export class MyApp extends Application {
constructor() {
super({shutdown: {signals: ['SIGINT']}});
this.component(AppComponent);
}
async main() {
const svc = await this.get(GREETING_SERVICE);
console.log(await svc.greet('en', 'World'));
}
}
Core Patterns Summary
| Pattern |
Key APIs |
When to Use |
| Context & Binding |
Context, bind(), toClass(), toDynamicValue(), toProvider() |
Managing artifacts and their dependencies |
| DI |
@inject(), @inject.getter(), @inject.view() |
Decoupling construction from behavior |
| Extension Point |
@extensionPoint(), @extensions(), @extensions.list(), extensionFor(), @injectable() |
Pluggable, open-ended feature sets |
| Interceptor |
@injectable(asGlobalInterceptor()), Provider<Interceptor> |
Cross-cutting concerns |
| Observer |
@lifeCycleObserver('group'), LifeCycleObserver |
Startup/shutdown hooks (group controls order) |
| Configuration |
@config(), @config.view(), app.configure() |
Runtime-configurable behavior |
| Component |
Component, components[], services[], bindings[] |
Composable packaging of artifacts |
Key Rules
- Always import from
@loopback/core, not @loopback/context
- Use
BindingKey.create<T>() for strongly-typed keys
- Extension injection:
@extensions() returns Getter<T[]> (lazy, picks up
dynamic additions); @extensions.list() returns T[] (eager, simpler when
extensions are static at startup)
- Use
@config() for artifact configuration, app.configure(key).to(value) to
set it
- Use
@injectable(bindingTemplate) to decorate extension classes — can combine
scope and extension:
@injectable({scope: BindingScope.SINGLETON}, extensionFor(POINT))
- Use
createBindingFromClass() to create bindings that respect @injectable
metadata
- Compose components hierarchically:
components[] for nesting, services[]
for auto-registration, bindings[] for custom bindings
- Use
BindingScope.SINGLETON for shared stateful services
- Use
CoreBindings.APPLICATION_INSTANCE to inject the Application itself
- Use
ContextTags.KEY to tag bindings with a stable key:
tags: {[ContextTags.KEY]: MY_KEY}
- Lifecycle observer groups are sorted alphabetically — use numbered prefixes
(e.g.,
'03-setup', '10-app') to control startup order
References
- Context & Bindings:
references/context-and-bindings.md —
creating contexts, binding types, scopes, finding bindings, context hierarchy,
views, components
- Dependency Injection:
references/dependency-injection.md —
constructor/property/method injection, getters, views, custom decorators,
custom injectors
- Extension Points:
references/extension-points.md — defining
contracts, extension point classes, implementing/registering extensions,
configuration
- Interceptors & Observers:
references/interceptors-and-observers.md
— global interceptors, interceptor proxies, life cycle observers, dynamic
config via ContextView
- Configuration: references/configuration.md
—
@config(), @config.view(), dynamic config, custom resolvers, sync vs
async
- Advanced Recipes:
references/advanced-recipes.md — custom
decorators, custom injectors, parameterized class factories, application
scaffolding
1---2name: loopback-core3description: Build large-scale, extensible Node.js applications and frameworks using LoopBack 4 core patterns. Use when building TypeScript/Node.js projects that need IoC containers, dependency injection, extension point/extension patterns, interceptors, life cycle observers, or component-based architecture. Triggers on tasks involving @loopback/core, @loopback/context, Context, Binding, @inject, @injectable, @extensionPoint, @extensions, LifeCycleObserver, Interceptor, or Component patterns. Also use when the user asks about structuring large-scale Node.js projects for extensibility and composability.4---56# Build Large-Scale Node.js Projects with LoopBack 4 Core78LoopBack 4 core provides an IoC container and DI framework in TypeScript9designed for async-first, large-scale Node.js applications. Import from10`@loopback/core` (not `@loopback/context`). This skill covers only11`@loopback/core` — not REST, repositories, or other LoopBack modules.1213## Architecture Decision Tree14151. **Need to manage artifacts and their dependencies?** -> Context & Bindings16 ([context-and-bindings.md](references/context-and-bindings.md))172. **Need loose coupling between artifact construction and behavior?** ->18 Dependency Injection19 ([dependency-injection.md](references/dependency-injection.md))203. **Need a pluggable system where others can add capabilities?** -> Extension21 Point/Extension ([extension-points.md](references/extension-points.md))224. **Need cross-cutting concerns (caching, logging, tracing)?** -> Interceptors23 ([interceptors-and-observers.md](references/interceptors-and-observers.md))245. **Need to hook into app start/stop?** -> Life Cycle Observers25 ([interceptors-and-observers.md](references/interceptors-and-observers.md))266. **Need runtime-configurable artifacts?** -> Configuration27 ([configuration.md](references/configuration.md))287. **Need custom decorators, parameterized classes, or advanced patterns?** ->29 Advanced Recipes ([advanced-recipes.md](references/advanced-recipes.md))3031## Quick Start: Minimal Extensible Application3233```ts34import {35 Application,36 BindingKey,37 Component,38 Binding,39 createBindingFromClass,40 extensionPoint,41 extensions,42 BindingTemplate,43 extensionFor,44 injectable,45 Getter,46 config,47} from '@loopback/core';4849// 1. Define the extension contract50export interface Greeter {51 language: string;52 greet(name: string): string;53}5455export const GREETER_EXTENSION_POINT_NAME = 'greeters';5657export const asGreeter: BindingTemplate = binding => {58 extensionFor(GREETER_EXTENSION_POINT_NAME)(binding);59 binding.tag({namespace: 'greeters'});60};6162// 2. Define the extension point63@extensionPoint(GREETER_EXTENSION_POINT_NAME)64export class GreetingService {65 constructor(66 @extensions() private getGreeters: Getter<Greeter[]>,67 @config() public readonly options?: {color: string},68 ) {}69 async greet(language: string, name: string): Promise<string> {70 const greeters = await this.getGreeters();71 const greeter = greeters.find(g => g.language === language);72 return greeter ? greeter.greet(name) : `Hello, ${name}!`;73 }74}7576// 3. Implement extensions77@injectable(asGreeter)78export class EnglishGreeter implements Greeter {79 language = 'en';80 greet(name: string) {81 return `Hello, ${name}!`;82 }83}8485@injectable(asGreeter)86export class ChineseGreeter implements Greeter {87 language = 'zh';88 greet(name: string) {89 return `${name},你好!`;90 }91}9293// 4. Bundle into a component94export const GREETING_SERVICE = BindingKey.create<GreetingService>(95 'services.GreetingService',96);9798export class GreetingComponent implements Component {99 bindings: Binding[] = [100 createBindingFromClass(GreetingService, {key: GREETING_SERVICE}),101 createBindingFromClass(EnglishGreeter),102 createBindingFromClass(ChineseGreeter),103 ];104}105106// 5. Compose components via nesting107export class CoreComponent implements Component {108 // services: auto-registered service/provider classes109 services = [SomeUtilityService];110}111112export class AppComponent implements Component {113 // components: nested components (registered recursively)114 components = [CoreComponent, GreetingComponent];115 // services: additional services for this layer116 services = [AppSpecificService];117}118119// 6. Wire up the application120export class MyApp extends Application {121 constructor() {122 super({shutdown: {signals: ['SIGINT']}});123 this.component(AppComponent);124 }125 async main() {126 const svc = await this.get(GREETING_SERVICE);127 console.log(await svc.greet('en', 'World'));128 }129}130```131132## Core Patterns Summary133134| Pattern | Key APIs | When to Use |135| ----------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------- |136| Context & Binding | `Context`, `bind()`, `toClass()`, `toDynamicValue()`, `toProvider()` | Managing artifacts and their dependencies |137| DI | `@inject()`, `@inject.getter()`, `@inject.view()` | Decoupling construction from behavior |138| Extension Point | `@extensionPoint()`, `@extensions()`, `@extensions.list()`, `extensionFor()`, `@injectable()` | Pluggable, open-ended feature sets |139| Interceptor | `@injectable(asGlobalInterceptor())`, `Provider<Interceptor>` | Cross-cutting concerns |140| Observer | `@lifeCycleObserver('group')`, `LifeCycleObserver` | Startup/shutdown hooks (group controls order) |141| Configuration | `@config()`, `@config.view()`, `app.configure()` | Runtime-configurable behavior |142| Component | `Component`, `components[]`, `services[]`, `bindings[]` | Composable packaging of artifacts |143144## Key Rules145146- Always import from `@loopback/core`, not `@loopback/context`147- Use `BindingKey.create<T>()` for strongly-typed keys148- Extension injection: `@extensions()` returns `Getter<T[]>` (lazy, picks up149 dynamic additions); `@extensions.list()` returns `T[]` (eager, simpler when150 extensions are static at startup)151- Use `@config()` for artifact configuration, `app.configure(key).to(value)` to152 set it153- Use `@injectable(bindingTemplate)` to decorate extension classes — can combine154 scope and extension:155 `@injectable({scope: BindingScope.SINGLETON}, extensionFor(POINT))`156- Use `createBindingFromClass()` to create bindings that respect `@injectable`157 metadata158- Compose components hierarchically: `components[]` for nesting, `services[]`159 for auto-registration, `bindings[]` for custom bindings160- Use `BindingScope.SINGLETON` for shared stateful services161- Use `CoreBindings.APPLICATION_INSTANCE` to inject the Application itself162- Use `ContextTags.KEY` to tag bindings with a stable key:163 `tags: {[ContextTags.KEY]: MY_KEY}`164- Lifecycle observer groups are sorted alphabetically — use numbered prefixes165 (e.g., `'03-setup'`, `'10-app'`) to control startup order166167## References168169- **Context & Bindings**:170 [references/context-and-bindings.md](references/context-and-bindings.md) —171 creating contexts, binding types, scopes, finding bindings, context hierarchy,172 views, components173- **Dependency Injection**:174 [references/dependency-injection.md](references/dependency-injection.md) —175 constructor/property/method injection, getters, views, custom decorators,176 custom injectors177- **Extension Points**:178 [references/extension-points.md](references/extension-points.md) — defining179 contracts, extension point classes, implementing/registering extensions,180 configuration181- **Interceptors & Observers**:182 [references/interceptors-and-observers.md](references/interceptors-and-observers.md)183 — global interceptors, interceptor proxies, life cycle observers, dynamic184 config via ContextView185- **Configuration**: [references/configuration.md](references/configuration.md)186 — `@config()`, `@config.view()`, dynamic config, custom resolvers, sync vs187 async188- **Advanced Recipes**:189 [references/advanced-recipes.md](references/advanced-recipes.md) — custom190 decorators, custom injectors, parameterized class factories, application191 scaffolding