Closed
Description
This spawned out of the discussion following #234 (comment). Pending the removal of the existing @Param(s)
decorators in #289. I would like to propose that we repurpose them as generic decorators that allow you to populate from arbitrary request options.
enum ParamType {
Params = 'params',
Query = 'query',
Body = 'body',
Header = 'headers',
// ...
}
interface ParamOptions {
required?: boolean = true,
allow?: ParamType | ParamType[] | { [key: ParamType]: string | string[] | boolean },
}
function Param(options: ParamOptions);
function Params(type: Type, validate: boolean | ValidatorOptions = true)
Parameter Usage
@JsonController()
class ProductsController {
@Get("/products")
@Get("/categories/:categoryId/products")
getProducts(
@Param({
required: false,
allow: ParamType.Query,
})
tagId: number, // req.query.tagId
@Param({
// required: true,
allow: [ ParamType.Path, ParamType.Query ],
})
categoryId: number, // req.params.categoryId || req.query.categoryId
@Param({
// required: true,
allow: {
[ParamType.Query]: [ 'api_key', 'apiKey' ],
[ParamType.Header]: 'X-Auth-Api-Key',
}
})
apiKey: string, // req.query.api_key || req.query.apiKey || req.header('x-auth-api-key')
) { /* ... */ }
}
Class-Based Usage
We can start by splitting out our authentication/session related parameters as it's likely that many services will require them:
class AuthParams {
@Param({
required: false,
allow: ParamType.Session,
})
user: UserSession;
@Param({
allow: {
[ParamType.Query]: [ 'api_key', 'apiKey' ],
[ParamType.Header]: 'X-Auth-Api-Key',
}
})
apiKey: string;
}
Next create a class to encapsulate your request definition (ideally these classes could support all class-validator
decorators as well):
class GetProductsByCategory {
@Param({
required: false,
allow: ParamType.Query,
})
tagId: number;
@Param({
allow: [ ParamType.Path, ParamType.Query ],
})
categoryId: number;
@Params() // could support nesting?
auth: AuthParams;
}
Now we can inject shared, class-based request definitions into our controller methods:
@JsonController()
class ProductsController {
@Get("/products")
@Get("/categories/:categoryId/products")
getProducts(
@Params(GetProductsByCategory) req: GetProductsByCategory,
@Params(AuthParams) auth: AuthParams,
) { }
}