kobp
Version:
Koa Boilerplate with MikroORM
92 lines (82 loc) • 2.96 kB
text/typescript
import type { KobpServiceContext, Middleware } from '../context'
import { KobpError, Loggy } from '..'
export type AuditMessagePipelineEventType = 'start' | 'success' | 'error'
/**
* AuditMessage pipeline callback.
*
* @param message - a original message that previous pipeline has produced.
* @param event - the event denote what has in particular event.
* @param context - the request's context.
* @param error - error computed by errorPipeline.
*/
export type AuditMessagePipeline = (message: string, event: AuditMessagePipelineEventType, context: KobpServiceContext, error?: Error) => string
const WithJson = () => {
const config: {
/**
* Ideal place to wrap the error before it emit via reponse.json()
*/
errorPipeline: ((err: any, loggy?: Loggy) => Error)[]
/**
* Run 2 times per API call.
*
* - First when API has been first called: event = 'start'
* - Second when API has deem service will returns which can be either: event = 'success' or 'error'
*/
auditMessagePipeline: AuditMessagePipeline[]
/**
* path that this middleware should ignored.
* Default to `/healthcheck`
*/
suppressPath: string
} = {
auditMessagePipeline: [],
errorPipeline: [],
suppressPath: `${process.env.KOBP_JSON_SILENT_PATH || ''}` || '/healthcheck'
}
const middleware = (): Middleware => {
// Assign logger if needed.
const suppressPathPattern = new RegExp(config.suppressPath, 'i')
return async function(ctx, next) {
const url = ctx.request.url
const loggy = suppressPathPattern.test(url) ? null : Loggy.current()
const auditMessage = (event: AuditMessagePipelineEventType, error?: Error): string => {
const httpStatus = event === 'start' ? '' : `${ctx.response?.status || ''}`
let msg = `${ctx.request.method} ${url} ${httpStatus}`
for(const pipe of config.auditMessagePipeline || []) {
msg = pipe(msg, event, ctx, error)
}
return msg
}
try {
loggy?.log(`[<<] ${auditMessage('start')}`)
await next()
loggy?.success(`[>>] ${auditMessage('success')}`)
} catch (err) {
// will only respond with JSON
let _err = err
if (config.errorPipeline) {
for(const pipe of config.errorPipeline) {
_err = pipe(_err, loggy)
}
}
ctx.status = _err.statusCode || _err.status || 500;
ctx.body = {
success: false,
code: _err.code && _err.code,
error: _err.message,
data: _err.data,
type: _err instanceof KobpError ? 'kobp' : undefined,
};
// Always logs error case
loggy?.failed(`[>>] ${auditMessage('error', _err)}`, _err)
}
}
}
return {
config,
middleware,
}
}
const instance = WithJson()
export const withJson = instance.middleware
export const withJsonConfig = instance.config