-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes: #27
- Loading branch information
Showing
8 changed files
with
296 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"flow": "gflow", | ||
"remote": "origin", | ||
"develop": "main", | ||
"production": "main", | ||
"ignores": [], | ||
"syncAfterFinish": false, | ||
"postFinish": "", | ||
"skipTest": false, | ||
"charReplacement": "-", | ||
"charBranchNameSeparator": "-", | ||
"branchTypes": { | ||
"feat": "feat", | ||
"fix": "fix", | ||
"chore": "chore", | ||
"docs": "docs" | ||
}, | ||
"refs": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import {DeserializerPipe} from "@tsed/platform-params"; | ||
import {JsonParameterStore, PipeMethods} from "@tsed/schema"; | ||
import {OverrideProvider} from "@tsed/di"; | ||
import {plainToClass} from "class-transformer"; | ||
|
||
@OverrideProvider(DeserializerPipe) | ||
export class ClassTransformerPipe implements PipeMethods { | ||
transform(value: any, metadata: JsonParameterStore) { | ||
return plainToClass(metadata.type, value); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import {ValidationError, ValidationPipe} from "@tsed/platform-params"; | ||
import {JsonParameterStore, PipeMethods} from "@tsed/schema"; | ||
import {OverrideProvider} from "@tsed/di"; | ||
import {plainToClass} from "class-transformer"; | ||
import {validate} from "class-validator"; | ||
|
||
@OverrideProvider(ValidationPipe) | ||
export class ClassValidationPipe extends ValidationPipe implements PipeMethods<any> { | ||
async transform(value: any, metadata: JsonParameterStore) { | ||
if (!this.shouldValidate(metadata)) { | ||
// there is no type and collectionType | ||
return value; | ||
} | ||
|
||
const object = plainToClass(metadata.type, value); | ||
const errors = await validate(object); | ||
|
||
if (errors.length > 0) { | ||
throw new ValidationError("Oops something is wrong", errors); | ||
} | ||
|
||
return value; | ||
} | ||
|
||
protected shouldValidate(metadata: JsonParameterStore): boolean { | ||
const types: Function[] = [String, Boolean, Number, Array, Object]; | ||
|
||
return !(metadata.type || metadata.collectionType) || !types.includes(metadata.type); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import {ObjectSchema} from "joi"; | ||
import {StoreSet} from "@tsed/core"; | ||
import {JoiValidationPipe} from "../pipes/JoiValidationPipe"; | ||
|
||
export function UseJoiSchema(schema: ObjectSchema) { | ||
return StoreSet(JoiValidationPipe, schema); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import {BodyParams} from "@tsed/platform-params"; | ||
import {Get} from "@tsed/schema"; | ||
import {Controller} from "@tsed/di"; | ||
import {UseJoiSchema} from "../decorators/UseJoiSchema"; | ||
import {joiPersonModel, PersonModel} from "../models/PersonModel"; | ||
|
||
@Controller("/persons") | ||
export class PersonsController { | ||
@Get(":id") | ||
async findOne( | ||
@BodyParams("id") | ||
@UseJoiSchema(joiPersonModel) | ||
person: PersonModel | ||
) { | ||
return person; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import {ObjectSchema} from "joi"; | ||
import {Injectable} from "@tsed/di"; | ||
import {JsonParameterStore, PipeMethods} from "@tsed/schema"; | ||
import {ValidationError, ValidationPipe} from "@tsed/platform-params"; | ||
|
||
@OverrideProvider(ValidationPipe) | ||
export class JoiValidationPipe implements PipeMethods { | ||
transform(value: any, metadata: JsonParameterStore) { | ||
const schema = metadata.store.get<ObjectSchema>(JoiValidationPipe); | ||
|
||
if (schema) { | ||
const {error} = schema.validate(value); | ||
|
||
if (error) { | ||
throw new ValidationError("Oops something is wrong", [error]); | ||
} | ||
} | ||
|
||
return value; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import {ValidationError, ValidationPipe} from "@tsed/platform-params"; | ||
import {JsonParameterStore, PipeMethods} from "@tsed/schema"; | ||
import {OverrideProvider} from "@tsed/di"; | ||
import {getJsonSchema} from "@tsed/schema"; | ||
import {validate} from "./validate"; | ||
|
||
@OverrideProvider(ValidationPipe) | ||
export class CustomValidationPipe extends ValidationPipe implements PipeMethods { | ||
public transform(obj: any, metadata: JsonParameterStore): void { | ||
// JSON service contain tool to build the Schema definition of a model. | ||
const schema = getJsonSchema(metadata.type); | ||
|
||
if (schema) { | ||
const valid = validate(schema, obj); | ||
|
||
if (!valid) { | ||
throw new ValidationError("My message", [ | ||
/// list of errors | ||
]); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
# Validation | ||
|
||
Ts.ED provide by default a [AJV](/tutorials/ajv.md) package `@tsed/ajv` to perform a validation on a [Model](/docs/model.md). | ||
|
||
This package must be installed to run automatic validation on input data. Any model used on parameter and annotated with one of JsonSchema decorator will be | ||
validated with AJV. | ||
|
||
::: code-group | ||
|
||
```sh [npm] | ||
npm install --save @tsed/ajv | ||
``` | ||
|
||
```sh [yarn] | ||
yarn add @tsed/ajv | ||
``` | ||
|
||
```sh [pnpm] | ||
pnpm add @tsed/ajv | ||
``` | ||
|
||
```sh [bun] | ||
bun add @tsed/ajv | ||
``` | ||
::: | ||
|
||
## Data input validation | ||
|
||
Ts.ED support the data input validation with the decorators provided by `@tsed/schema`. | ||
|
||
Example: | ||
|
||
<<< @/docs/snippets/controllers/request-input-validation.ts | ||
|
||
## Custom Validation | ||
|
||
Ts.ED allows you to change the default @@ValidationPipe@@ by your own library. The principle is simple. | ||
Create a CustomValidationPipe and use @@OverrideProvider@@ to change the default @@ValidationPipe@@. | ||
|
||
::: warning | ||
Replace the default JsonSchema validation provided by Ts.ED isn't recommended. You lose the ability to generate the swagger documentation and the json-mapper feature. | ||
::: | ||
|
||
<<< @/docs/snippets/validation/validator-pipe.ts | ||
|
||
::: warning | ||
Don't forgot to import the new `CustomValidatorPipe` in your `server.ts` ! | ||
::: | ||
|
||
### Use Joi | ||
|
||
There are several approaches available for object validation. One common approach is to use schema-based validation. | ||
The [Joi](https://github.com/hapijs/joi) library allows you to create schemas in a pretty straightforward way, with a readable API. | ||
|
||
Let's look at a pipe that makes use of Joi-based schemas. | ||
|
||
Start by installing the required package: | ||
|
||
::: code-group | ||
|
||
```sh [npm] | ||
npm install --save joi | ||
``` | ||
|
||
```sh [yarn] | ||
yarn add joi | ||
``` | ||
|
||
```sh [pnpm] | ||
pnpm add joi | ||
``` | ||
|
||
```sh [bun] | ||
bun add joi | ||
``` | ||
|
||
::: | ||
|
||
In the code sample below, we create a simple class that takes a schema as a constructor argument. | ||
We then apply the `schema.validate()` method, which validates our incoming argument against the provided schema. | ||
|
||
In the next section, you'll see how we supply the appropriate schema for a given controller method using the @@UsePipe@@ decorator. | ||
|
||
<<< @/docs/snippets/validation/joi-pipe.ts | ||
|
||
Now, we have to create a custom decorator to store the Joi schema along with a parameter: | ||
|
||
<<< @/docs/snippets/validation/joi-pipe-decorator.ts | ||
|
||
And finally, we are able to add Joi schema with our new decorator: | ||
|
||
<<< @/docs/snippets/validation/joi-pipe-usage.ts | ||
|
||
### Use Class validator | ||
|
||
Let's look at an alternate implementation of our validation technique. | ||
|
||
Ts.ED works also with the [class-validator](https://github.com/typestack/class-validator) library. | ||
This library allows you to use **decorator-based** validation (like Ts.ED with his [JsonSchema](/docs/models) decorators). | ||
Decorator-based validation combined with Ts.ED [Pipe](/docs/pipes.html) capabilities since we have access to the medata.type of the processed parameter. | ||
|
||
Before we start, we need to install the required packages: | ||
|
||
::: code-group | ||
|
||
```sh [npm] | ||
npm i --save class-validator class-transformer | ||
``` | ||
|
||
```sh [yarn] | ||
yarn add class-validator class-transformer | ||
``` | ||
|
||
```sh [pnpm] | ||
pnpm add class-validator class-transformer | ||
``` | ||
|
||
```sh [bun] | ||
bun add class-validator class-transformer | ||
``` | ||
|
||
::: | ||
|
||
Once these are installed, we can add a few decorators to the `PersonModel`: | ||
|
||
```typescript | ||
import {IsString, IsInt} from "class-validator"; | ||
|
||
export class CreateCatDto { | ||
@IsString() | ||
firstName: string; | ||
|
||
@IsInt() | ||
age: number; | ||
} | ||
``` | ||
|
||
::: tip | ||
Read more about the class-validator decorators [here](https://github.com/typestack/class-validator#usage). | ||
::: | ||
|
||
Now we can create a [ClassValidationPipe] class: | ||
|
||
<<< @/docs/snippets/validation/class-validator-pipe.ts | ||
|
||
::: warning Notice | ||
Above, we have used the [class-transformer](https://github.com/typestack/class-transformer) library. | ||
It's made by the same author as the **class-validator** library, and as a result, they play very well together. | ||
::: | ||
|
||
Note that we get the type from @@ParamMetadata@@ and give it to plainToObject function. The method `shouldValidate` | ||
bypass the validation process for the basic types and when the `metadata.type` or `metadata.collectionType` are not available. | ||
|
||
Next, we use the **class-transformer** function `plainToClass()` to transform our plain JavaScript argument object into a typed object | ||
so that we can apply validation. The incoming body, when deserialized from the network request, does not have any type information. | ||
Class-validator needs to use the validation decorators we defined for our **PersonModel** earlier, | ||
so we need to perform this transformation. | ||
|
||
Finally, we return the value when we haven't errors or throws a `ValidationError`. | ||
|
||
::: tip | ||
If you use **class-validator**, it also be logical to use [class-transformer](https://github.com/typestack/class-transformer) as Deserializer. | ||
So we recommend to override also the @@DeserializerPipe@@. | ||
|
||
<<< @/docs/snippets/validation/class-transformer-pipe.ts | ||
::: | ||
|
||
We just have to import the pipe on our `server.ts` and use model as type on a parameter. |