UNPKG

@feathersjs/schema

Version:

A common data schema definition format

87 lines (75 loc) 2.72 kB
import { HookContext, NextFunction } from '@feathersjs/feathers' import { BadRequest } from '@feathersjs/errors' import { VALIDATED } from '@feathersjs/adapter-commons' import { Schema, Validator } from '../schema' import { DataValidatorMap } from '../json-schema' /** * Options for {@link validateQuery}. */ export type ValidateQueryOptions = { /** * When `true` (the default), a successfully validated query marks the query * so adapters skip their built-in operator and filter allowlist * (`sanitizeQuery`). The schema is then the full allowlist. * * Set to `false` to still run adapter sanitization after schema validation * (defense in depth). Both layers then apply: the schema must accept the * query, and the adapter must still allow every `$` operator and filter. * * @default true */ skipSanitize?: boolean } export const validateQuery = <H extends HookContext>( schema: Schema<any> | Validator, options: ValidateQueryOptions = {} ) => { const { skipSanitize = true } = options const validator: Validator = typeof schema === 'function' ? schema : schema.validate.bind(schema) return async (context: H, next?: NextFunction) => { const data = context?.params?.query || {} try { const query = await validator(data) // Marking as VALIDATED tells AdapterBase.sanitizeQuery to skip its allowlist. // Opt out with skipSanitize: false to run both layers. if (skipSanitize) { Object.defineProperty(query, VALIDATED, { value: true }) } context.params = { ...context.params, query } } catch (error: any) { throw error.ajv ? new BadRequest(error.message, error.errors) : error } if (typeof next === 'function') { return next() } } } export const validateData = <H extends HookContext>(schema: Schema<any> | DataValidatorMap | Validator) => { return async (context: H, next?: NextFunction) => { const data = context.data const validator = typeof (schema as Schema<any>).validate === 'function' ? (schema as Schema<any>).validate.bind(schema) : typeof schema === 'function' ? schema : (schema as any)[context.method] if (validator) { try { if (Array.isArray(data)) { context.data = await Promise.all(data.map((current) => validator(current))) } else { context.data = await validator(data) } Object.defineProperty(context.data, VALIDATED, { value: true }) } catch (error: any) { throw error.ajv ? new BadRequest(error.message, error.errors) : error } } if (typeof next === 'function') { return next() } } }