I would like to use Typescript interfaces to easily validate backend requests. Is there a way to make it happen? Example of what I would love to do:
import { Request, Response } from 'express'
interface AuthenticateParams {
email: string
password: string
}
type AuthenticationRequest = Request & {body: AuthenticateParams}
const authenticate = async (req: AuthenticationRequest, res: Response) => {
if (typeof req.body !== AuthenticateParams) res.status(400).send('Invalid body')
// DO STUFF
let stuff
res.status(200).send(stuff)
}
export default authenticate
Manual validation The most basic validation, it's a set of conditions that check whether the structure is the expected one. const validate = (data: ExampleType) => { if (! data. pets) return false; // perform more checks return true; }; const getBiped = async () => { const data = await fetchData(); console.
Joi can be used for creating schemas (just like we use mongoose for creating NoSQL schemas) and you can use it with plain Javascript objects. It's like a plug n play library and is easy to use. On the other hand, express-validator uses validator. js to validate expressjs routes, and it's mainly built for express.
According to the official website, Express Validator is a set of Express. js middleware that wraps validator. js , a library that provides validator and sanitizer functions. Simply said, Express Validator is an Express middleware library that you can incorporate in your apps for server-side data validation.
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:
const 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
import { 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
import { 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 love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With