Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the best way to automatically validate requests using Typescript?

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
like image 382
M. Hav Avatar asked Nov 14 '19 16:11

M. Hav


People also ask

How to validate data in TypeScript?

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.

Which is better express validator or Joi?

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.

What is express validator?

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.


1 Answers

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.

like image 79
M. Hav Avatar answered Oct 30 '22 07:10

M. Hav