There are a few advanced usages of the LoopBack core modules that are not covered by the Greeter Extension and Greeter Application example.
Create your own decorator
You can create your own decorator:
-
Create a new decorator from scratch by using
DecoratorFactory
from@loopback/core
. See an example in custom-inject-decorator.ts -
Create a sugar decorator for an existing decorator.
export function globalInterceptor(group?: string) { injectable({tags: [ContextTags.GLOBAL_INTERCEPTOR]}); }
Create your own injector
You can also create your own injector. See the dependency injection docs page for more details.
export function env(name: string) {
return inject('', {resolve: () => process.env[name]});
}
For a complete example, see https://github.com/loopbackio/loopback-next/blob/master/examples/context/src/custom-inject-decorator.ts.
Class factory to allow parameterized decorations
Since decorations applied on a top-level class cannot have references to variables, you might want to create a class factory that allows parameterized decorations.
function createControllerClass(version: string, basePath: string) {
@api({basePath: `${basePath}`})
class Controller {
@get(`/${version}`) find() {}
}
}
For a complete example, see parameterized-decoration.ts.
Trigger dependency injection with an explicit context
class InjectionHelper {
constructor(@inject(RestBindings.Http.REQUEST))
public readonly request: Request) {}
}
const interceptor: Interceptor = (invocationCtx, next) => {
const helper = instantiateClass(invocationCtx, InjectionHelper);
const request = helper.request;
});
Magic ValueOrPromise
For the dependency injection framework, there are two flavors: synchronous and asynchronous.
Context.getSync()
resolves a value synchronously, whereas Context.get()
allows you to resolve the value asynchronous and returns a Promise
.
When ValueOrPromise
is being used in a value provider, if the value is
produced synchronously, a value will be returned, otherwise a Promise will be
return. When multiple dependencies are involved to resolve the value for a
binding, a Promise
will be returned if at least one of the bindings produces a
Promise
.
Using the ChineseGreeter
as an example, it has a dependency of the
configuration object to be injected as instructed by @config
.
/**
* A greeter implementation for Chinese.
*/
@injectable(asGreeter)
export class ChineseGreeter implements Greeter {
language = 'zh';
constructor(
/**
* Inject the configuration for ChineseGreeter
*/
@config()
private options: ChineseGreeterOptions = {nameFirst: true},
) {}
greet(name: string) {
if (this.options && this.options.nameFirst === false) {
return `你好,${name}!`;
}
return `${name},你好!`;
}
}
There are two ways to configure the greeter.
Option 1
app.configure('greeters.ChineseGreeter').to({nameFirst: false});
We call app.getSync('greeters.ChineseGreeter')
or
app.get('greeters.ChineseGreeter')
to get the ChineseGreeter
.
Option 2
app
.configure('greeters.ChineseGreeter')
.toDynamicValue(async () => ({nameFirst: false}));
We can only call app.get('greeters.ChineseGreeter')
because the configuration
dependencies is asynchronous. Please note app.getSync()
will throw an
exception to indicate that the ChineseGreeter binding cannot be resolved
synchronously.
More examples
For more examples, refer the Context example.
Previous: Part 9 - Boot by convention