kobp
Version:
Koa Boilerplate with MikroORM
75 lines • 3.24 kB
JavaScript
import { ClientErrorCode, KobpError, Loggy } from '../utils';
import { METADATA_KEYS, extractSchema } from './doc.helpers';
/**
* This will mutates the inputs
* `query` will mutates context.query
* `params` will mutates context.params
* `body` will mutates context.request.body
* `headers` will mutates context.request.headers
*/
export const withValidation = (schemaSpec) => {
const fn = async (context, next) => {
// validate them one-by-one [Params => Query => Body]
const keys = ['headers', 'query', 'params', 'body'];
for (const locationKey of keys) {
const spec = schemaSpec[locationKey];
if (!spec) {
continue;
}
try {
switch (locationKey) {
case 'query':
context.query = spec.parse(context.query);
break;
case 'params':
context.params = spec.parse(context.params);
break;
case 'body':
context.request.body = spec.parse(context.request.body);
break;
case 'headers':
context.request.headers = spec.parse(context.request.headers);
break;
}
}
catch (err) {
Loggy.error(`Validation error: "${err}"`, err);
// try construct the useful message
let errorMessage = `${err?.errorMessage || err?.message || err}`;
// Identify the path based on 2 libraries (ajv-ts, zod)
// ZodError
const zodIssues = err.issues;
// AJV-ts error
const ajvErrorPath = err.cause?.error?.instancePath || null;
let orgSchemaValidationData = {};
if (zodIssues) {
errorMessage = zodIssues
.map((issue) => {
return `${[locationKey, ...issue.path].join('.')} ${issue.message}`;
})
.join(', ');
orgSchemaValidationData = zodIssues;
}
else if (ajvErrorPath) {
const path = ajvErrorPath.replace(/^\//, `${locationKey}.`).replaceAll('/', '.');
errorMessage = `Input error on: ${path} ${errorMessage}`;
orgSchemaValidationData = err.cause || {};
}
throw KobpError.fromUserInput(ClientErrorCode.badRequest, errorMessage, orgSchemaValidationData);
}
}
await next();
};
// This should print only once.
for (const key of ['headers', 'params', 'query', 'body']) {
const spec = schemaSpec[key];
if (!spec)
continue;
const [_source, schema] = extractSchema(spec);
// console.log(`defined ${key} schema::`, source, schema)
// save this to internal storage against its function.
Reflect.defineMetadata(METADATA_KEYS[`DOC_${key.toUpperCase()}_SHAPE_VALIDATION_KEY`], schema, fn);
}
return fn;
};
//# sourceMappingURL=withValidation.js.map