» npm install validate-typescript
Videos
» npm install @types/validator
UPDATE 2022:
I stumbled upon Runtypes and I think it is quite a cool solution. And when you think of it, quite an obvious one: You use a solution in the middle that automatically gives you correct types and a possibility to check the validity. It's much better than trying to work from types to validating, since interfaces don't exist in runtime as was said multiple times.
ORIGINAL ANSWER:
Yeah, I understand that interfaces don't exist in runtime. I created this solution, that validates my input and gives me type errors, when I need them. I think my title was bit misleading, so I'll also change the title to "What is the best way to automatically validate requests using Typescript".
The idea is to force me to write a simple schema for each interface I wish to use to validate a backend endpoint. Also, in order to minimize the possibility to human errors, it is important that my linter type checks the validatorObject.
validateParams.ts:
Copyconst validateParams: (targetObject: any, validatorObject: any) => boolean = (targetObject, validatorObject) => {
if (typeof targetObject !== typeof validatorObject) return false
if (typeof targetObject === 'object') {
let validObject = true
if (Array.isArray(targetObject)) {
for (let subObject of targetObject) {
validObject = validObject && validateParams(subObject, validatorObject[0])
}
} else {
for (let key of Object.keys(validatorObject)) {
if (typeof targetObject[key] === 'object') validObject = validObject && validateParams(targetObject[key], validatorObject[key])
if (typeof targetObject[key] !== typeof validatorObject[key]) validObject = false
}
}
return validObject
}
return true
}
export default validateParams
authenticate.ts
Copyimport { Request, Response } from 'express'
import validateParams from "./validateParams"
interface AuthenticateParams {
email: string
password: string
}
const validatorObject: AuthenticateParams = {
email: 'string',
password: 'string'
}
type AuthenticationRequest = Request & {body: AuthenticateParams}
const authenticate = async (req: AuthenticationRequest, res: Response) => {
if (!validateParams(req.body, validatorObject)) res.status(400).send('Invalid body')
// DO STUFF
let stuff
res.status(200).send(stuff)
}
export default authenticate
updateUser.ts
Copyimport { Request, Response } from 'express'
import validateParams from "./validateParams"
interface UpdateUserParams {
name: string
products: {
name: string
price: number
}[]
}
const validatorObject: UpdateUserParams = {
name: 'string',
products: [{name: 'string', number: 0}]
}
type AuthenticationRequest = Request & {body: UpdateUserParams}
const updateUser = async (req: UpdateUserParams, res: Response) => {
if (!validateParams(req.body, validatorObject)) res.status(400).send('Invalid body')
// DO STUFF
let stuff
res.status(200).send(stuff)
}
export default updateUser
This is not the most beautiful solution, but I like that it validates my requests automatically and also gives me errors, if I forget to update my validatorObject. But yeah, it would be awesome to get the Typescript interfaces in runtime.
If you also maintaining openapi documentation, you can validate the request using express-openapi-validator. It is also available for fastify and Koa
» npm install typescript-validator
» npm install validator.ts
This question is old, but I'd like to share my validation library also.
It's type-script friendly, tiny (no tons of unnecessary functionality) and easily extensible by custom validators.
npm: https://www.npmjs.com/package/checkeasy
github: https://github.com/smbwain/checkeasy
import {alternatives, arrayOf, int, object, oneOf, optional, string} from 'checkeasy';
const myValidator = object({
a: int({max: 5}),
b: string(),
c: optional(float()),
d: oneOf(['a', 'b', 7] as const),
e: alternatives([string(), int()]),
f: arrayOf(string()),
g: object({
subP: string(),
}),
});
const value = myValidator(anyUnsafeData, 'name');
// type of value is: {
// a: number,
// b: string,
// c: number | undefined,
// d: "a" | "b" | 7,
// e: string | number,
// f: string[],
// g: {subP: string},
//}
It also throws clear human readable messages in errors. E.g.
myValidator({a: 'hello'}, 'data');
// throws: [data.a] should be an integer
myValidator({a: 1, b: 'string', d: 'a', e: true}, 'data');
// throws: All alternatives failed for [data.e]:
// [data.e.@alternative(0)] should be a string
// [data.e.@alternative(1)] should be an integer
I've created a super lightweight library called Smoke Screen which does exactly that. It leverages typescript features to perform any kind of object validation within javascript runtime. It's not 100% seamless due to the fact that javascript does not hold any type information at runtime, but thanks to TypeScript decorators, this may be easily done:
class Person {
@exposed({type: Number})
age: number;
}
// serialize a Person object into a JSON string
const person = new Person();
person.age = 56.8;
const smokeScreen = new SmokeScreen();
smokeScreen.toJSON(person); // -> '{"age":56.8}'
// deserialize a JSON string into a Person object
let json = JSON.stringify({age: 19});
const person2 = smokeScreen.fromJSON(json, Person);
console.log(person2); // -> Person { age: 19 }
// typing validation
json = JSON.stringify({age: "oops"});
smokeScreen.fromJSON(json, Person); // Error: illegal input - property 'age' must be a number
Additional custom validators may be set, optional parameters and null checking are also supported and enforced. I suggest reading more about it and trying it out.
» npm install just-validate