Skip to content

Instantly share code, notes, and snippets.

@renmu123
Created November 18, 2024 02:05
Show Gist options
  • Save renmu123/de7d51addcb51358d12b2c368498fb8c to your computer and use it in GitHub Desktop.
Save renmu123/de7d51addcb51358d12b2c368498fb8c to your computer and use it in GitHub Desktop.
koa validator
import originValidators from "validator";
import { Context, Next } from "koa";
type ValidatorFunction = (value: any, args?: any) => boolean;
interface CustomValidators {
[key: string]: ValidatorFunction;
}
const customValidators: CustomValidators = {
isString: (value) => {
return typeof value === "string";
},
isArray: (value) => {
return Array.isArray(value);
},
isObject: (value) => {
return typeof value === "object";
},
isNumber: (value) => {
return typeof value === "number";
},
isInt: (value) => {
return Number.isInteger(value);
},
};
const validators = { ...originValidators, ...customValidators };
interface ValidationRule {
method: string;
args?: any;
message: string;
}
class ValidationChain {
ctx: Context;
chain: ValidationRule[];
fieldType: string;
field: string;
constructor() {
this.chain = [];
this.fieldType = "";
this.field = "";
}
validate(value: any): { field: string; message: string }[] {
const field = this.field;
const errors: { field: string; message: string }[] = [];
for (const rule of this.chain) {
const methodName = rule.method;
let newValue = value;
if (!customValidators[methodName]) {
newValue = value + "";
}
const validateMethod = validators[methodName];
if (!validateMethod(newValue, rule.args)) {
errors.push({ field: field, message: rule.message });
break;
}
}
return errors;
}
body(field: string): this {
this.field = field;
this.fieldType = "body";
return this;
}
query(field: string): this {
this.field = field;
this.fieldType = "query";
return this;
}
add(method: string, args: any, message: string): this {
this.chain.push({ method, args, message });
return this;
}
isInt({ opts, message }: { opts?: any; message?: string } = {}): this {
this.add("isInt", opts, message ?? "Invalid integer");
return this;
}
isNumber({ message }: { message?: string } = {}): this {
this.add("isNumber", {}, message ?? "Invalid number");
return this;
}
isString({ message }: { message?: string } = {}): this {
this.add("isString", {}, message ?? "Invalid string");
return this;
}
isArray({ message }: { message?: string } = {}): this {
this.add("isArray", {}, message ?? "Invalid array");
return this;
}
isObject({ message }: { message?: string } = {}): this {
this.add("isObject", {}, message ?? "Invalid object");
return this;
}
}
const validate = (schema: ValidationChain[]) => {
return async (ctx: Context, next: Next) => {
const body = ctx.request.body;
const query = ctx.request.query;
const errors: { field: string; message: string }[] = [];
for (const chain of schema) {
const field = chain.field;
const data = chain.fieldType === "body" ? body : query;
console.log("data", data, field);
errors.push(...chain.validate(data[field]));
}
if (errors.length > 0) {
ctx.status = 400;
ctx.body = {
errors: errors,
};
console.log("errors", errors);
} else {
await next();
}
};
};
function body(field: string): ValidationChain {
const chain = new ValidationChain();
chain.body(field);
return chain;
}
function query(field: string): ValidationChain {
const chain = new ValidationChain();
chain.query(field);
return chain;
}
export { validate, body, query };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment