Skip to main content

Custom Decorator

Custom Decorator is a powerful function in Summer that can let you extract request params, do auth, method interception, modify request results, and more...

Create APIs

Create MethodDescriptionUsage
createParamDecoratorused in controller method paramextract request data
createMethodDecoratorused in methodmethod interception
createClassDecoratorused in classclass methods interception
createClassAndMethodDecoratorused in class/methodclass methods interception
createPropertyDecoratorused in propertyinject config, init property data

Create Param Decorator

export const [DecoratorName] = createParamDecorator(
(ctx?, paramName?, arg1?, arg2?,...) => {
return [Value]
}
)

DecoratorName decorator name
ctx request context
paramName param name defined in method
arg1, arg2... custom params used in decorator like @DecoratorName(arg1,arg2,...)

Get the AppVersion value in request header
import { Controller, createParamDecorator, Get } from '@summer-js/summer'

export const AppVersion = createParamDecorator((ctx) => {
return ctx.request.headers['app-version']
})

@Controller
export class AppVersionController {
@Get('/app/version')
async version(@AppVersion version) {
return version
}
}
parse uid in JWT token
import { Controller, createParamDecorator, Get } from '@summer-js/summer'
import jwt from 'jsonwebtoken'

export const Uid = createParamDecorator((ctx) => {
const token = ctx.request.headers['authentication']
try {
const decoded = jwt.verify(token, 'xxxxxxxx')
return decoded.uid
} catch (e) {}
return null
})

@Controller
export class JWTController {
@Get('/userinfo')
userinfo(@Uid uid?: number) {
return uid
}
}

Create Class/Method Decorator

// only works in METHOD
export const [DecoratorName] = createMethodDecorator(
async (ctx?, invokeMethod? , arg1?, arg2?,...) => {
return await invokeMethod(ctx.invocation.params)
}
);

// only works in CLASS
export const [DecoratorName] = createClassDecorator(
async (ctx?, invokeMethod? , arg1?, arg2?,...) => {
return await invokeMethod(ctx.invocation.params)
}
);

// works in CLASS / METHOD
export const [DecoratorName] = createClassAndMethodDecorator(
async (ctx?, invokeMethod? , arg1?, arg2?,...) => {
return await invokeMethod(ctx.invocation.params)
}
);

DecoratorName decorator name
ctx request context
invokeMethod method for invoking
arg1, arg2... custom param used in this decorator like @DecoratorName(arg1,arg2,...)

info

Write a class decorator means intercept all it's methods

Develop a @RequireLogin decorator
import { Controller, createClassAndMethodDecorator, Get, Put } from '@summer-js/summer'
import jwt from 'jsonwebtoken'

export const RequireLogin = createClassAndMethodDecorator(async (ctx, invokeMethod?) => {
const token = ctx.request.headers['authentication']
try {
jwt.verify(token, 'xxxxxxxx')
return await invokeMethod(ctx.invocation.params)
} catch (e) {
ctx.response.statusCode = 401
ctx.response.body = 'Unauthorized'
}
})

@Controller
@RequireLogin
export class LoginController {
@Get('/me')
info() {}

@Put('/me')
update() {}
}

@Controller
export class LoginController2 {
@Get('/users/:id')
userInfo() {}

@RequireLogin
@Put('/userinfo')
update() {}
}

Create Property Decorator

export const [DecoratorName] = createPropertyDecorator(
(config?, propertyName? , arg1?, arg2?,...) => {
return [Value]
}
);

DecoratorName decorator name
config current evn config
propertyName property name
arg1, arg2... custom param used in this decorator like @DecoratorName(arg1,arg2,...)

read mysql config
import { Controller, createPropertyDecorator, Get } from '@summer-js/summer'

export const MySQLConfig = createPropertyDecorator((config) => {
return config['MySQL_CONFIG']
})

@Controller
export class ConfigInjectController {
@MySQLConfig
mysqlConfig

@Get('/mysql-host')
host() {
return this.mysqlConfig.host
}
}
read city list
import { Controller, createPropertyDecorator, Get } from '@summer-js/summer'

export const CityList = createPropertyDecorator(() => {
return ['Shanghai', 'Tokyo', 'New York City']
})

@Controller
export class CityListController {
@CityList
cityList

@Get('/cities')
list() {
return this.cityList
}
}

More Example

Alter Response Http Code

import { Controller, createMethodDecorator, Get } from '@summer-js/summer'

export const ResponseCode = createMethodDecorator(
async (ctx, invokeMethod, code: number) => {
ctx.response.statusCode = code
return await invokeMethod(ctx.invocation.params)
}
)

@Controller
export class ResponseCodeController {
@ResponseCode(404)
@Get('/dog')
userInfo() {
return 'dog not exist'
}
}

Cache Return Value

import { AutoInject, Controller, createMethodDecorator, Get, Logger, PathParam, Service } from '@summer-js/summer'
import md5 from 'md5'

const CACHE = {}
export const Cache = createMethodDecorator(async (ctx, invokeMethod, cacheTime: number) => {
const callParamHash = md5(JSON.stringify(ctx.invocation))
if (CACHE[callParamHash] === undefined) {
CACHE[callParamHash] = await invokeMethod(ctx.invocation.params)
Logger.info('Cache ' + CACHE[callParamHash] + ' in ' + cacheTime + 's')
if (cacheTime) {
setTimeout(() => {
CACHE[callParamHash] = undefined
Logger.info('Clear Cache')
}, cacheTime)
}
}
return CACHE[callParamHash]
})

@Service
export class CacheService {
@Cache
async cache(id) {
return id + ':' + Date.now()
}
}

@Controller
export class CacheController {
cacheService: CacheService

@Get('/cache/:id')
@Cache(1000)
async api(@PathParam id) {
return id + ':' + Date.now()
}

@Get('/cache2/:id')
async api2(@PathParam id) {
return this.cacheService.cache(id)
}
}

Download File

import { Controller, createMethodDecorator, Get } from '@summer-js/summer'

export const DownLoadFile = createMethodDecorator(
async (ctx, invokeMethod, fileName: string) => {
ctx.response.headers['Content-Type'] = 'application/octet-stream'
ctx.response.headers['Content-Disposition'] = `attachment; filename="${fileName}"`
return await invokeMethod(ctx.invocation.params)
}
)

@Controller
export class DownloadController {
@DownLoadFile('hello.txt')
@Get('/download')
download() {
return 'Hello Summer'
}
}