Easy validation with Nest.js and Joi
Hi everybody, hope you’re safe at home 🏠
Today a quick article about automated data validation with Joi and Nest.js !
This article assumes that you have basic knowledge about :
- Nest.js and its dependency injection mechanism
- Joi and automated data validation
- Express and middlewares
- TypeScript and Decorators feature
What is this all about ?
So typical use case here is to build an automated way to validate the data sent to your API routes.
That’s what Joi already provides as a feature, by allowing you to describe the structure of you data. Other libraries exist too of course like ajv, class-validator, and probably many others.
Typically you would define your data validation definition, or schema as they are called, this way :
import * as Joi from '@hapi/joi'
const schema = Joi.object({
mandatory: Joi.string().required() // some required string
optional: Joi.string().optional() // some optional string
})
That’s a very nice and declarative way to define what your data should look like, and allows for checking that any object abides by this definition.
What if I told you that it also comes bundled as a TypeScript Decorator thanks to Joiful ? Nice indeed 😌
This allows you to turn your previous definition into :
import * as Joiful from 'joiful'
class Schema {
@Joiful.string().required() // some required string
mandatory!: string
@Joiful.string().optional() // some optional string
optional?: string
}
As you can foresee, that might be nice to group many features directly in your classes definitions as decorators are by nature composables.
To illustrate my point you could think of, for example, combining with decorators from other libraries like TypeORM :
import * as Joi from 'joiful'
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'@Entity()
export class Client {
@PrimaryGeneratedColumn()
@Joiful.number().optional()
id?: number @Column()
@Joiful.string().required()
mandatory!: string @Column({ nullable: true })
@Joiful.string().optional()
optional?: string
}
What about being able to automatically forward everything sent to your Controller’s routes for validation ?
It’s actually called Pipes !
The documentation already state about implementing validation with Joi but the point here is to go a little bit further.
Let’s code !
Ok let’s build some basic, but illustrative, example 👐
Our example will implement :
- automatic validation from a Joiful decorated class
- automatic validation from a mix of Joi schema(s) and/or Joiful decorated class(es)
First let’s define some dummy validation schemas :
Main goal is to have your route decorated this way :
- By automatically deducting the schema to validate with from the Joiful decorator class itself :
- By providing either explicit Joiful decorated class(es) and/or Joi schema(s) :
As we are good kids, let’s start by implementing it with Test-Driven-Development 😎
So here we are, let’s implement the Nest.js validation Pipe :
Let’s stop for a bit and check how this works 🤓
First Pipes have to implements the PipeTransform
interface :
Which implies it requires a transform
method :
To get to the point quickly, it first checks if there’s any metadata.metatype
(which is the case when providing it implicitly with a Joiful annotated class) or if it has been provided with schemas
(when explicitly providing a bunch of mixed Joiful annotated classes and Joi schema definitions).
As you can see in this implementation explicit declaration will always take precedence over implicit one, but you could implement it differently of course.
Then if it’s explicitly defined :
In this case if the value is an Array
it might means an array containing items of the explicitly defined definitions, but the developer might want to have control over it after all, hence the wrapSchemaAsArray
to give the developer the opportunity for example to directly provide some Joi.array(...)
definition of his/her own.
Of course in that simple implementation, it is left to the developer to provide Joi schema definitions that are mergeable together.
Speaking about merging schemas, let’s have a look at how to do it :
What happens here is that if we have a class annotated with Joiful decorators we retrieve its Joi definition via getJoiSchema
, once left with Joi schemas we’re able to merge them together with concat
.
Last but not least, we can differentiate Joi schema from Joiful annotated class by checking if propertyisJoi === true
. As you can see here, Joiful annotated class type is infered to Constructor<any>
, while Joi schema is infered as Joi.Schema
.
Otherwise if it’s implicitly defined :
This case is a bit different of the previous one because it’s currently not possible to directly defineImplicit[]
as a type in the route’s method signature. Not matter this limitation, it makes sense to assume that if it’s an Array
it should implicitly validate each of its item with the class Joiful decorators.
A special note too :
The reason here to specify @Optional()
decorator is that it makes constructor’s parameters optional and therefore allows you to use the Pipe with these 2 syntaxes :
Otherwise you would be required to specify new ValidationPipe()
even if you don’t intend to specify any parameter.
And that’s all folks ! 🐰🥕
Hope you enjoyed it,
Do not hesitate to either comment 💬 or show some love 👏 😉
Stay safe & happy coding 💻
Also please note that the full source code is available here :