Issue
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
Solution
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.
Answered By - M. Hav
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.