@shadow-library/fastify 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -0
- package/cjs/module/fastify.utils.d.ts +9 -2
- package/cjs/module/fastify.utils.js +15 -10
- package/esm/module/fastify.utils.d.ts +9 -2
- package/esm/module/fastify.utils.js +15 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -850,6 +850,55 @@ export class AppModule {}
|
|
|
850
850
|
- **Database**: Add database connection decorators
|
|
851
851
|
- **Caching**: Configure caching plugins
|
|
852
852
|
|
|
853
|
+
### Customizing AJV Validation
|
|
854
|
+
|
|
855
|
+
The module uses [AJV](https://ajv.js.org/) for JSON Schema validation. You can customize the AJV instance by providing custom options or plugins through the `ajv` configuration:
|
|
856
|
+
|
|
857
|
+
```typescript
|
|
858
|
+
import { Module } from '@shadow-library/app';
|
|
859
|
+
import { FastifyModule } from '@shadow-library/fastify';
|
|
860
|
+
import ajvFormats from 'ajv-formats';
|
|
861
|
+
import ajvKeywords from 'ajv-keywords';
|
|
862
|
+
|
|
863
|
+
@Module({
|
|
864
|
+
imports: [
|
|
865
|
+
FastifyModule.forRoot({
|
|
866
|
+
controllers: [UserController],
|
|
867
|
+
ajv: {
|
|
868
|
+
// Custom AJV options
|
|
869
|
+
customOptions: {
|
|
870
|
+
removeAdditional: 'all',
|
|
871
|
+
coerceTypes: 'array',
|
|
872
|
+
useDefaults: true,
|
|
873
|
+
},
|
|
874
|
+
// AJV plugins - supports both formats:
|
|
875
|
+
// 1. Just the plugin function (uses empty options)
|
|
876
|
+
// 2. Tuple of [plugin, options]
|
|
877
|
+
plugins: [
|
|
878
|
+
ajvFormats, // Plugin without options
|
|
879
|
+
[ajvKeywords, { keywords: ['typeof', 'instanceof'] }], // Plugin with options
|
|
880
|
+
],
|
|
881
|
+
},
|
|
882
|
+
}),
|
|
883
|
+
],
|
|
884
|
+
})
|
|
885
|
+
export class AppModule {}
|
|
886
|
+
```
|
|
887
|
+
|
|
888
|
+
#### AJV Configuration Options
|
|
889
|
+
|
|
890
|
+
| Option | Type | Description |
|
|
891
|
+
| --------------- | ------------------------------------------ | ----------------------------------------------------- |
|
|
892
|
+
| `customOptions` | `object` | Custom AJV options to merge with the default settings |
|
|
893
|
+
| `plugins` | `Array<Plugin \| [Plugin, PluginOptions]>` | Array of AJV plugins to register with both validators |
|
|
894
|
+
|
|
895
|
+
#### Common Use Cases for AJV Customization:
|
|
896
|
+
|
|
897
|
+
- **Format Validation**: Add `ajv-formats` for email, uri, date-time validation
|
|
898
|
+
- **Custom Keywords**: Use `ajv-keywords` for additional validation keywords
|
|
899
|
+
- **Strict Mode**: Configure strict schema validation settings
|
|
900
|
+
- **Type Coercion**: Customize how types are coerced during validation
|
|
901
|
+
|
|
853
902
|
## Middleware
|
|
854
903
|
|
|
855
904
|
Create custom middleware by implementing the `Middleware` decorator:
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { ValidationError } from '@shadow-library/common';
|
|
2
|
-
import { SchemaObject } from 'ajv';
|
|
2
|
+
import Ajv, { SchemaObject } from 'ajv';
|
|
3
3
|
import { FastifyInstance } from 'fastify';
|
|
4
4
|
import { FastifyRouteSchemaDef, FastifySchemaValidationError, FastifyValidationResult, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
5
5
|
import { FastifyConfig, FastifyModuleOptions } from './fastify-module.interface.js';
|
|
6
|
+
/**
|
|
7
|
+
* Defining types
|
|
8
|
+
*/
|
|
9
|
+
export interface AjvValidators {
|
|
10
|
+
strictValidator: Ajv;
|
|
11
|
+
lenientValidator: Ajv;
|
|
12
|
+
}
|
|
6
13
|
export declare const notFoundHandler: () => never;
|
|
7
|
-
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject
|
|
14
|
+
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject>, validators: AjvValidators): FastifyValidationResult;
|
|
8
15
|
export declare function formatSchemaErrors(errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar): ValidationError;
|
|
9
16
|
export declare function createFastifyInstance(config: FastifyConfig, fastifyFactory?: FastifyModuleOptions['fastifyFactory']): Promise<FastifyInstance>;
|
|
@@ -18,17 +18,13 @@ const fastify_1 = require("fastify");
|
|
|
18
18
|
* Importing user defined packages
|
|
19
19
|
*/
|
|
20
20
|
const server_error_1 = require("../server.error.js");
|
|
21
|
-
/**
|
|
22
|
-
* Defining types
|
|
23
|
-
*/
|
|
24
21
|
/**
|
|
25
22
|
* Declaring the constants
|
|
26
23
|
*/
|
|
27
24
|
const keywords = ['x-fastify'];
|
|
28
25
|
const allowedHttpParts = ['body', 'params', 'querystring'];
|
|
29
|
-
const strictValidator = new ajv_1.default({ allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
30
|
-
const lenientValidator = new ajv_1.default({ allErrors: true, coerceTypes: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
31
26
|
const notFoundError = new server_error_1.ServerError(server_error_1.ServerErrorCode.S002);
|
|
27
|
+
const defaultAjvOptions = { allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords };
|
|
32
28
|
const notFoundHandler = () => (0, common_1.throwError)(notFoundError);
|
|
33
29
|
exports.notFoundHandler = notFoundHandler;
|
|
34
30
|
function compileSchema(ajv, schema) {
|
|
@@ -41,13 +37,13 @@ function compileSchema(ajv, schema) {
|
|
|
41
37
|
}
|
|
42
38
|
return ajv.getSchema(schema.$id);
|
|
43
39
|
}
|
|
44
|
-
function compileValidator(routeSchema) {
|
|
40
|
+
function compileValidator(routeSchema, validators) {
|
|
45
41
|
(0, node_assert_1.default)(allowedHttpParts.includes(routeSchema.httpPart), `Invalid httpPart: ${routeSchema.httpPart}`);
|
|
46
42
|
if (routeSchema.httpPart === 'body')
|
|
47
|
-
return compileSchema(strictValidator, routeSchema.schema);
|
|
43
|
+
return compileSchema(validators.strictValidator, routeSchema.schema);
|
|
48
44
|
if (routeSchema.httpPart === 'params')
|
|
49
|
-
return compileSchema(lenientValidator, routeSchema.schema);
|
|
50
|
-
const validate = compileSchema(lenientValidator, routeSchema.schema);
|
|
45
|
+
return compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
46
|
+
const validate = compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
51
47
|
return (data) => {
|
|
52
48
|
validate(data);
|
|
53
49
|
for (const error of validate.errors ?? []) {
|
|
@@ -78,10 +74,19 @@ function formatSchemaErrors(errors, dataVar) {
|
|
|
78
74
|
async function createFastifyInstance(config, fastifyFactory) {
|
|
79
75
|
const options = common_1.utils.object.omitKeys(config, ['port', 'host', 'errorHandler', 'responseSchema']);
|
|
80
76
|
const { errorHandler } = config;
|
|
77
|
+
const strictValidator = new ajv_1.default({ ...defaultAjvOptions, ...config.ajv?.customOptions });
|
|
78
|
+
const lenientValidator = new ajv_1.default({ ...defaultAjvOptions, coerceTypes: true, ...config.ajv?.customOptions });
|
|
79
|
+
for (let plugin of config.ajv?.plugins ?? []) {
|
|
80
|
+
if (typeof plugin === 'function')
|
|
81
|
+
plugin = [plugin, {}];
|
|
82
|
+
const [ajvPlugin, options] = plugin;
|
|
83
|
+
ajvPlugin(strictValidator, options);
|
|
84
|
+
ajvPlugin(lenientValidator, options);
|
|
85
|
+
}
|
|
81
86
|
const instance = (0, fastify_1.fastify)(options);
|
|
82
87
|
instance.setSchemaErrorFormatter(formatSchemaErrors);
|
|
83
88
|
instance.setNotFoundHandler(exports.notFoundHandler);
|
|
84
89
|
instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
|
|
85
|
-
instance.setValidatorCompiler(compileValidator);
|
|
90
|
+
instance.setValidatorCompiler(routeSchema => compileValidator(routeSchema, { strictValidator, lenientValidator }));
|
|
86
91
|
return fastifyFactory ? await fastifyFactory(instance) : instance;
|
|
87
92
|
}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { ValidationError } from '@shadow-library/common';
|
|
2
|
-
import { SchemaObject } from 'ajv';
|
|
2
|
+
import Ajv, { SchemaObject } from 'ajv';
|
|
3
3
|
import { FastifyInstance } from 'fastify';
|
|
4
4
|
import { FastifyRouteSchemaDef, FastifySchemaValidationError, FastifyValidationResult, SchemaErrorDataVar } from 'fastify/types/schema';
|
|
5
5
|
import { FastifyConfig, FastifyModuleOptions } from './fastify-module.interface.js';
|
|
6
|
+
/**
|
|
7
|
+
* Defining types
|
|
8
|
+
*/
|
|
9
|
+
export interface AjvValidators {
|
|
10
|
+
strictValidator: Ajv;
|
|
11
|
+
lenientValidator: Ajv;
|
|
12
|
+
}
|
|
6
13
|
export declare const notFoundHandler: () => never;
|
|
7
|
-
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject
|
|
14
|
+
export declare function compileValidator(routeSchema: FastifyRouteSchemaDef<SchemaObject>, validators: AjvValidators): FastifyValidationResult;
|
|
8
15
|
export declare function formatSchemaErrors(errors: FastifySchemaValidationError[], dataVar: SchemaErrorDataVar): ValidationError;
|
|
9
16
|
export declare function createFastifyInstance(config: FastifyConfig, fastifyFactory?: FastifyModuleOptions['fastifyFactory']): Promise<FastifyInstance>;
|
|
@@ -9,17 +9,13 @@ import { fastify } from 'fastify';
|
|
|
9
9
|
* Importing user defined packages
|
|
10
10
|
*/
|
|
11
11
|
import { ServerError, ServerErrorCode } from '../server.error.js';
|
|
12
|
-
/**
|
|
13
|
-
* Defining types
|
|
14
|
-
*/
|
|
15
12
|
/**
|
|
16
13
|
* Declaring the constants
|
|
17
14
|
*/
|
|
18
15
|
const keywords = ['x-fastify'];
|
|
19
16
|
const allowedHttpParts = ['body', 'params', 'querystring'];
|
|
20
|
-
const strictValidator = new Ajv({ allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
21
|
-
const lenientValidator = new Ajv({ allErrors: true, coerceTypes: true, useDefaults: true, removeAdditional: true, strict: true, keywords });
|
|
22
17
|
const notFoundError = new ServerError(ServerErrorCode.S002);
|
|
18
|
+
const defaultAjvOptions = { allErrors: true, useDefaults: true, removeAdditional: true, strict: true, keywords };
|
|
23
19
|
export const notFoundHandler = () => throwError(notFoundError);
|
|
24
20
|
function compileSchema(ajv, schema) {
|
|
25
21
|
if (!schema.$id)
|
|
@@ -31,13 +27,13 @@ function compileSchema(ajv, schema) {
|
|
|
31
27
|
}
|
|
32
28
|
return ajv.getSchema(schema.$id);
|
|
33
29
|
}
|
|
34
|
-
export function compileValidator(routeSchema) {
|
|
30
|
+
export function compileValidator(routeSchema, validators) {
|
|
35
31
|
assert(allowedHttpParts.includes(routeSchema.httpPart), `Invalid httpPart: ${routeSchema.httpPart}`);
|
|
36
32
|
if (routeSchema.httpPart === 'body')
|
|
37
|
-
return compileSchema(strictValidator, routeSchema.schema);
|
|
33
|
+
return compileSchema(validators.strictValidator, routeSchema.schema);
|
|
38
34
|
if (routeSchema.httpPart === 'params')
|
|
39
|
-
return compileSchema(lenientValidator, routeSchema.schema);
|
|
40
|
-
const validate = compileSchema(lenientValidator, routeSchema.schema);
|
|
35
|
+
return compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
36
|
+
const validate = compileSchema(validators.lenientValidator, routeSchema.schema);
|
|
41
37
|
return (data) => {
|
|
42
38
|
validate(data);
|
|
43
39
|
for (const error of validate.errors ?? []) {
|
|
@@ -68,10 +64,19 @@ export function formatSchemaErrors(errors, dataVar) {
|
|
|
68
64
|
export async function createFastifyInstance(config, fastifyFactory) {
|
|
69
65
|
const options = utils.object.omitKeys(config, ['port', 'host', 'errorHandler', 'responseSchema']);
|
|
70
66
|
const { errorHandler } = config;
|
|
67
|
+
const strictValidator = new Ajv({ ...defaultAjvOptions, ...config.ajv?.customOptions });
|
|
68
|
+
const lenientValidator = new Ajv({ ...defaultAjvOptions, coerceTypes: true, ...config.ajv?.customOptions });
|
|
69
|
+
for (let plugin of config.ajv?.plugins ?? []) {
|
|
70
|
+
if (typeof plugin === 'function')
|
|
71
|
+
plugin = [plugin, {}];
|
|
72
|
+
const [ajvPlugin, options] = plugin;
|
|
73
|
+
ajvPlugin(strictValidator, options);
|
|
74
|
+
ajvPlugin(lenientValidator, options);
|
|
75
|
+
}
|
|
71
76
|
const instance = fastify(options);
|
|
72
77
|
instance.setSchemaErrorFormatter(formatSchemaErrors);
|
|
73
78
|
instance.setNotFoundHandler(notFoundHandler);
|
|
74
79
|
instance.setErrorHandler(errorHandler.handle.bind(errorHandler));
|
|
75
|
-
instance.setValidatorCompiler(compileValidator);
|
|
80
|
+
instance.setValidatorCompiler(routeSchema => compileValidator(routeSchema, { strictValidator, lenientValidator }));
|
|
76
81
|
return fastifyFactory ? await fastifyFactory(instance) : instance;
|
|
77
82
|
}
|
package/package.json
CHANGED