Careeroza
How Metadata and Guards Work
Metadata, Reflector and a Guard Chain
Example
SetMetadata stores a value on the method, Reflector reads it, and guards run one after another; the first one that fails stops the request. A runnable version with legacy method decorators (a Map plays the role of reflect-metadata):
const metadata = new Map<Function, Map<string, unknown>>();
function SetMetadata(key: string, value: unknown): MethodDecorator {
return (_target, _name, descriptor) => {
const fn = descriptor.value as unknown as Function;
const map = metadata.get(fn) ?? new Map<string, unknown>();
map.set(key, value);
metadata.set(fn, map);
};
}
const Roles = (...roles: string[]) => SetMetadata('roles', roles);
const reflector = { get: <T>(key: string, fn: Function) => metadata.get(fn)?.get(key) as T | undefined };
class ProductsController {
findAll() { return 'list'; }
@Roles('admin') remove() { return 'deleted'; }
}
type User = { name: string; roles: string[] } | null;
type Ctx = { user: User; handler: Function };
type Guard = (ctx: Ctx) => true | string;
const authGuard: Guard = (ctx) => (ctx.user ? true : '401 Unauthorized');
const rolesGuard: Guard = (ctx) => {
const required = reflector.get<string[]>('roles', ctx.handler);
if (!required) return true;
return required.some((r) => ctx.user!.roles.includes(r)) ? true : '403 Forbidden resource';
};
function dispatch(user: User, method: 'findAll' | 'remove') {
const controller = new ProductsController();
const ctx: Ctx = { user, handler: ProductsController.prototype[method] };
for (const guard of [authGuard, rolesGuard]) {
const result = guard(ctx);
if (result !== true) return result;
}
return '200 ' + controller[method]();
}
const priya = { name: 'Priya', roles: ['customer'] };
const admin = { name: 'Arjun', roles: ['admin'] };
console.log('guest findAll:', dispatch(null, 'findAll'));
console.log('priya findAll:', dispatch(priya, 'findAll'));
console.log('priya remove :', dispatch(priya, 'remove'));
console.log('admin remove :', dispatch(admin, 'remove'));Output
guest findAll: 401 Unauthorized priya findAll: 200 list priya remove : 403 Forbidden resource admin remove : 200 deleted