
Security News
npm Adopts OIDC for Trusted Publishing in CI/CD Workflows
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
@loopback/metadata
Advanced tools
Utilities to help developers implement TypeScript decorators, define/merge metadata, and inspect metadata
This module contains utilities to help developers implement TypeScript decorators, define/merge metadata, and inspect metadata.
import {ClassDecoratorFactory} from '@loopback/metadata';
export interface MyClassMetadata {
name: string;
description?: string;
}
function myClassDecorator(spec: MyClassMetadata): ClassDecorator {
return ClassDecoratorFactory.createDecorator<MyClassMetadata>(
'metadata-key-for-my-class-decorator',
spec,
{decoratorName: '@myClassDecorator'},
);
}
Alternatively, we can instantiate the factory and create a decorator:
function myClassDecorator(spec: MyClassMetadata): ClassDecorator {
const factory = new ClassDecoratorFactory<MyClassMetadata>(
'metadata-key-for-my-class-decorator',
spec,
);
return factory.create();
}
Now we can use @myClassDecorator
to add metadata to a class as follows:
@myClassDecorator({name: 'my-controller'})
class MyController {}
import {MethodDecoratorFactory} from '@loopback/metadata';
export interface MyMethodMetadata {
name: string;
description?: string;
}
function myMethodDecorator(spec: MyMethodMetadata): MethodDecorator {
return MethodDecoratorFactory.createDecorator<MyMethodMetadata>(
'metadata-key-for-my-method-decorator',
spec,
);
}
Now we can use @myMethodDecorator
to add metadata to a method as follows:
class MyController {
@myMethodDecorator({name: 'my-method'})
myMethod(x: string): string {
return 'Hello, ' + x;
}
@myMethodDecorator({name: 'another-method'})
anotherMethod() {}
@myMethodDecorator({name: 'my-static-method'})
static myStaticMethod() {}
}
Instead of a single immutable object to be merged, the
MethodMultiDecoratorFactory
reduced parameters into a flat array of items.
When fetching the metadata later, you will receive it as an array.
import {MethodMultiDecoratorFactory} from '@loopback/metadata';
function myMultiMethodDecorator(spec: object): MethodDecorator {
return MethodMultiDecoratorFactory.createDecorator<object>(
'metadata-key-for-my-method-multi-decorator',
spec,
);
}
Now, you can use it multiple times on a method:
class MyController {
@myMultiMethodDecorator({x: 1})
@myMultiMethodDecorator({y: 2})
@myMultiMethodDecorator({z: 3})
public point() {}
}
class MyOtherController {
@myMultiMethodDecorator([{x: 1}, {y: 2}, {z: 3}])
public point() {}
}
And when you access this data:
const arrayOfSpecs = MetadataInspector.getMethodMetadata<object>(
'metadata-key-for-my-method-multi-decorator',
constructor.prototype,
op,
);
// [{z: 3}, {y: 2}, {x: 1}]
Typescript
applies decorators in reverse order
per class, from the parent down. The metadata array returned by getOwnMetadata
will be in this order:
class Parent {
@myMultiMethodDecorator('A') // second
@myMultiMethodDecorator('B') // first
public greet() {}
}
class Child extends Parent {
@myMultiMethodDecorator(['C', 'D']) // [third, fourth]
public greet() {}
}
class Grandchild extends Child {
@myMultiMethodDecorator('E') // sixth
@myMultiMethodDecorator('F') // fifth
public greet() {}
}
// getMethodMetadata = ['B', 'A', 'C', 'D', 'F', 'E']
You can also create a decorator that takes an object that can contain an array:
interface Point {
x?: number;
y?: number;
z?: number;
}
interface GeometryMetadata {
points: Point[];
}
function geometry(...points: Point[]): MethodDecorator {
return MethodMultiDecoratorFactory.createDecorator<GeometryMetadata>(
'metadata-key-for-my-method-multi-decorator',
points,
);
}
class MyGeoController {
@geometry({x: 1})
@geometry({x: 2}, {y: 3})
@geometry({z: 5})
public abstract() {}
}
const arrayOfSpecs = MetadataInspector.getMethodMetadata<GeometryMetadata>(
'metadata-key-for-my-method-multi-decorator',
constructor.prototype,
op,
);
// [
// { points: [{x: 1}]},
// { points: [{x:2}, {y:3}]},
// { points: [{z: 5}]},
// ]
import {PropertyDecoratorFactory} from '@loopback/metadata';
export interface MyPropertyMetadata {
name: string;
description?: string;
}
function myPropertyDecorator(spec: MyPropertyMetadata): PropertyDecorator {
return PropertyDecoratorFactory.createDecorator<MyPropertyMetadata>(
'metadata-key-for-my-property-decorator',
spec,
);
}
Now we can use @myPropertyDecorator
to add metadata to a property as follows:
class MyController {
@myPropertyDecorator({name: 'my-property'})
myProperty: string;
@myPropertyDecorator({name: 'another-property'})
anotherProperty: boolean;
@myPropertyDecorator({name: 'my-static-property'})
static myStaticProperty: string;
}
import {ParameterDecoratorFactory} from '@loopback/metadata';
export interface MyParameterMetadata {
name: string;
description?: string;
}
function myParameterDecorator(spec: MyParameterMetadata): ParameterDecorator {
return ParameterDecoratorFactory.createDecorator<MyParameterMetadata>(
'metadata-key-for-my-parameter-decorator',
spec,
);
}
Now we can use @myParameterDecorator
to add metadata to a parameter as
follows:
class MyController {
constructor(
@myParameterDecorator({name: 'logging-prefix'}) public prefix: string,
@myParameterDecorator({name: 'logging-level'}) public level: number,
) {}
myMethod(
@myParameterDecorator({name: 'x'}) x: number,
@myParameterDecorator({name: 'y'}) y: number,
) {}
static myStaticMethod(
@myParameterDecorator({name: 'a'}) a: string,
@myParameterDecorator({name: 'b'}) b: string,
) {}
}
import {MethodParameterDecoratorFactory} from '@loopback/metadata';
export interface MyParameterMetadata {
name: string;
description?: string;
}
function myMethodParameterDecorator(
spec: MyParameterMetadata,
): MethodDecorator {
return MethodParameterDecoratorFactory.createDecorator<MyParameterMetadata>(
'metadata-key-for-my-method-parameter-decorator',
spec,
);
}
Now we can use @myMethodParameterDecorator
to add metadata to a parameter as
follows:
class MyController {
@myMethodParameterDecorator({name: 'x'})
@myMethodParameterDecorator({name: 'y'})
myMethod(x: number, y: number) {}
}
WARNING: Using method decorators to provide metadata for parameters is strongly discouraged for a few reasons:
We recommend that ParameterDecorator
be used instead.
An object of type DecoratorOptions
can be passed in to create decorator
functions. There are two flags for the options:
true
.spec
argument will be cloned.
Sometimes we use shared spec for the decoration, but the decorator function
might need to mutate the object. Cloning the input spec makes it safe to use
the same spec (template
) to decorate different members. Default to true
.@inject
for error and
debugging messages.By default, the decorator factories allow inheritance with the following rules:
spec
argument from the
decorator function into the inherited value from base classes. For metadata
of array and other primitive types, the spec
argument is used if provided.
inherit
method of the decorator factory to customize how
to resolve spec
against the inherited metadata. For example:protected inherit(inheritedMetadata: T | undefined | null): T {
// Ignore the inherited metadata
return this.spec;
}
{
// Class level metadata
'my-class-decorator-key': MyClassMetadata,
// Static method (including the constructor) parameter metadata
'my-static-parameter-decorator-key': {
'': [MyConstructorParameterMetadata], // Constructor parameter metadata
'myStaticMethod1': [MyStaticMethodParameterMetadata],
'myStaticMethod2': [MyStaticMethodParameterMetadata],
},
// Static method metadata
'my-static-method-decorator-key': {
'myStaticMethod1': MyStaticMethodMetadata,
'myStaticMethod2': MyStaticMethodMetadata,
},
// Static property metadata
'my-static-property-decorator-key': {
'myStaticMethod1': MyStaticPropertyMetadata,
'myStaticMethod1': MyStaticPropertyMetadata,
}
}
{
// Instance method parameter metadata
'my-instance-parameter-decorator-key': {
'myMethod1': [MyMethodParameterMetadata],
'myMethod2': [MyMethodParameterMetadata],
},
// Instance method metadata
'my-instance-method-decorator-key': {
'myMethod1': MyMethodMetadata,
'myMethod2': MyMethodMetadata,
},
// Instance property metadata
'my-instance-property-decorator-key': {
'myProperty1': MyPropertyMetadata,
'myProperty2': MyPropertyMetadata,
}
}
The following methods in DecoratorFactory
allow subclasses to customize how to
merge the spec
with existing metadata for a class, methods, properties, and
method parameters. Please note M
is a map for methods/properties/parameters.
protected mergeWithInherited(
inheritedMetadata: M,
target: Object,
member?: string,
descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
): M {
// ...
}
protected mergeWithOwn(
ownMetadata: M,
target: Object,
member?: string,
descriptorOrIndex?: TypedPropertyDescriptor<any> | number,
): M {
// ...
}
@myClassDecorator({name: 'my-controller'})
@myClassDecorator({name: 'your-controller'})
class MyController {}
MetadataInspector
provides API to inspect metadata from a class and its
members.
import {MetadataInspector} from '@loopback/metadata';
const meta = MetadataInspector.getClassMetadata(
'my-class-decorator-key',
MyController,
);
import {MetadataInspector} from '@loopback/metadata';
const meta = MetadataInspector.getClassMetadata<MyClassMetaData>(
'my-class-decorator-key',
MyController,
{
ownMetadataOnly: true,
},
);
import {MetadataInspector} from '@loopback/metadata';
const allMethods = MetadataInspector.getAllMethodMetaData<MyMethodMetadata>(
'my-method-decorator-key',
MyController.prototype, // Use MyController for static methods
);
const myMethod = MetadataInspector.getMethodMetaData<MyMethodMetadata>(
'my-method-decorator-key',
MyController.prototype, // Use MyController for static methods
'myMethod',
);
import {MetadataInspector} from '@loopback/metadata';
const allProps = MetadataInspector.getAllPropertyMetaData<MyPropertyMetadata>(
'my-property-decorator-key',
MyController.prototype, // Use MyController for static properties
);
const myProp = MetadataInspector.getMethodMetaData<MyMethodMetadata>(
'my-property-decorator-key',
MyController.prototype, // Use MyController for static properties
'myProp',
);
import {MetadataInspector} from '@loopback/metadata';
const allParamsForMyMethod =
MetadataInspector.getAllParameterMetaData<MyParameterMetadata>(
'my-parameter-decorator-key',
MyController.prototype, // Use MyController for static methods,
'myMethod',
);
const firstParamForMyMethod =
MetadataInspector.getMyParameterMetaData<MyParameterMetadata>(
'my-parameter-decorator-key',
MyController.prototype, // Use MyController for static methods
'myMethod',
0, // parameter index
);
const allParamsForConstructor =
MetadataInspector.getAllParameterMetaData<MyParameterMetadata>(
'my-parameter-decorator-key',
MyController,
'',
);
You can use MetadataAccessor to provide type checks for metadata access via keys. For example,
const CLASS_KEY = MetadataAccessor.create<MyClassMetadata, ClassDecorator>(
'my-class-decorator-key',
);
// Create a class decorator with the key
const myClassDecorator = ClassDecoratorFactory.createDecorator(CLASS_KEY);
// Inspect a class with the key
const myClassMeta = MetadataInspector.getClassMetaData(CLASS_KEY, MyController);
Please note MetadataKey can be an instance of MetadataAccessor or a string.
import {MetadataInspector} from '@loopback/metadata';
const myPropType = MetadataInspector.getDesignTypeForProperty(
MyController.prototype,
'myProp',
);
const myConstructor = MetadataInspector.getDesignTypeForMethod(
MyController,
'',
);
const myMethod = MetadataInspector.getDesignTypeForMethod(
MyController.prototype, // Use MyController for static methods
'myMethod',
);
npm install --save @loopback/metadata
Run npm test
from the root folder.
See all contributors.
MIT
FAQs
Utilities to help developers implement TypeScript decorators, define/merge metadata, and inspect metadata
The npm package @loopback/metadata receives a total of 22,882 weekly downloads. As such, @loopback/metadata popularity was classified as popular.
We found that @loopback/metadata demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 7 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
npm now supports Trusted Publishing with OIDC, enabling secure package publishing directly from CI/CD workflows without relying on long-lived tokens.
Research
/Security News
A RubyGems malware campaign used 60 malicious packages posing as automation tools to steal credentials from social media and marketing tool users.
Security News
The CNA Scorecard ranks CVE issuers by data completeness, revealing major gaps in patch info and software identifiers across thousands of vulnerabilities.