Skip to content

Commit 420dedb

Browse files
authored
Merge pull request #6 from KirillTregubov/upgrade-changes
[1.10.0] deprecate route exclude, add disabled
2 parents d229466 + 4915ed7 commit 420dedb

8 files changed

+576
-165
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto eol=lf

README.md

+26-15
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,26 @@
77
<img width="250" src="./assets/images/badge.png">
88
</p>
99

10-
This plugin enables you to enforce `response`, `body` and `params` schemas on your controllers. The sentiment behind this is that no endpoint should ever be left without validation, and now you can enforce this on your application level, so no endpoints are released without the validation.
10+
This plugin enables you to enforce `response`, `body` and `params` schemas on your controllers. The sentiment behind this is that no endpoint should ever be left without validation, and now you can enforce this during their registration, so no endpoints are released without validation.
1111

12-
The plugin works by "hooking" into the [`onRoute hook`](https://www.fastify.io/docs/latest/Reference/Hooks/#onroute) which as described in the docs, triggers when a new route is registered.
12+
The plugin works by "hooking" into the [`onRoute Fastify hook`](https://www.fastify.io/docs/latest/Reference/Hooks/#onroute) which, as described in the docs, triggers when a new route is registered.
1313

1414
_This plugin is built together with our [Programmer Network](https://programmer.network/) Community. You can join us on [Twitch](https://twitch.tv/programmer_network) and [Discord](https://discord.gg/ysnpXnY7ba)._
1515

1616
## Install
1717

18-
Using [npm](https://nodejs.org/en/):
18+
### Requirements
1919

20-
- `npm i fastify-enforce-schema`
20+
- [Fastify](https://www.npmjs.com/package/fastify) v4.x
2121

22-
Using [yarn](https://yarnpkg.com/):
22+
### From [`npm`](https://www.npmjs.com/fastify-enforce-schema)
2323

24-
- `yarn add fastify-enforce-schema`
24+
```
25+
npm install fastify-enforce-schema # npm
26+
yarn add fastify-enforce-schema # yarn
27+
pnpm add fastify-enforce-schema # pnpm
28+
bun add fastify-enforce-schema # bun
29+
```
2530

2631
## Usage
2732

@@ -33,9 +38,14 @@ import Fastify from 'fastify'
3338
import enforceSchema from 'fastify-enforce-schema'
3439

3540
const fastify = Fastify()
41+
42+
// Register the plugin
3643
await fastify.register(enforceSchema, {
37-
// options
44+
// options (described below)
3845
})
46+
47+
// Register your routes
48+
// your route definitions here...
3949
```
4050
> _Note_: top-level await requires Node.js 14.8.0 or later
4151
@@ -44,9 +54,12 @@ await fastify.register(enforceSchema, {
4454
const fastify = require('fastify')()
4555
const enforceSchema = require('fastify-enforce-schema')
4656

57+
// Register the plugin
4758
fastify.register(enforceSchema, {
48-
// options
59+
// options (described below)
4960
})
61+
62+
// Register your routes
5063
fastify.register((fastify, options, done) => {
5164
// your route definitions here...
5265

@@ -85,22 +98,20 @@ By default, all schemas are enforced where appropriate.
8598
### Disable schema enforcement on specific routes
8699

87100
```js
88-
// Exclude all schemas
101+
// Disable all schemas
89102
fastify.get('/foo', { schema: false }, (req, reply) => {
90103
reply.code(200)
91104
})
92105

93-
// Exclude response and params schemas
106+
// Disable response and params schemas
94107
fastify.get(
95-
'/bar:baz',
96-
{ schema: { response: false, params: false } },
97-
(req, reply) => {
108+
'/bar:baz', { schema: { disabled: ['response', 'params'] } }, (req, reply) => {
98109
reply.code(200)
99110
}
100111
)
101112

102-
// Exclude body schema
103-
fastify.post('/baz', { schema: { body: false } }, (req, reply) => {
113+
// Disable body schema
114+
fastify.post('/baz', { schema: { disabled: ['body'] } }, (req, reply) => {
104115
reply.code(200)
105116
})
106117
```

index.js

+103-26
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const fp = require('fastify-plugin')
22
const {
3+
ErrorPrefix,
34
getErrorMessage,
45
hasProperties,
56
initialExcludes,
@@ -9,19 +10,13 @@ const {
910
} = require('./utils')
1011

1112
function FastifyEnforceSchema (fastify, opts, done) {
12-
if (Object.prototype.hasOwnProperty.call(opts, 'required')) {
13-
process.emitWarning(
14-
'The `required` option for fastify-enforce-schema will be removed soon. Since all schemas are enforced by default, consider using the `exclude` option to exclude specific schemas.',
15-
'DeprecationWarning'
16-
)
17-
} else {
18-
opts.required = []
19-
}
20-
2113
if (!Object.prototype.hasOwnProperty.call(opts, 'disabled')) {
2214
opts.disabled = []
2315
}
24-
if (opts.disabled === true || (Array.isArray(opts.disabled) && isSchemaDisabled(opts.disabled))) {
16+
if (
17+
opts.disabled === true ||
18+
(Array.isArray(opts.disabled) && isSchemaDisabled(opts.disabled))
19+
) {
2520
done()
2621
return
2722
}
@@ -42,8 +37,8 @@ function FastifyEnforceSchema (fastify, opts, done) {
4237
return
4338
}
4439

45-
const excludedEntity = [...initialExcludes, ...exclude].find(
46-
({ url }) => new RegExp(url).test(routeOptions.path)
40+
const excludedEntity = [...initialExcludes, ...exclude].find(({ url }) =>
41+
new RegExp(url).test(routeOptions.path)
4742
)
4843

4944
const hasSchemas =
@@ -59,59 +54,141 @@ function FastifyEnforceSchema (fastify, opts, done) {
5954
throw new Error(getErrorMessage({ schema: true }, routeOptions))
6055
}
6156

57+
if (
58+
Object.prototype.hasOwnProperty.call(routeOptions.schema, 'disabled') &&
59+
!Array.isArray(routeOptions.schema.disabled)
60+
) {
61+
throw new Error(
62+
getErrorMessage(
63+
{ message: 'schema.disabled must be an array' },
64+
routeOptions
65+
)
66+
)
67+
}
68+
6269
if (
6370
routeOptions?.schema?.response !== false &&
71+
!routeOptions?.schema?.disabled?.includes(SCHEMA_TYPES.response) &&
6472
!isSchemaTypeExcluded(excludedEntity, SCHEMA_TYPES.response) &&
6573
disabled.indexOf(SCHEMA_TYPES.response) === -1
6674
) {
6775
const responseKeys = Object.keys(routeOptions?.schema?.response || {})
6876

6977
if (!routeOptions?.schema?.response) {
70-
throw new Error(getErrorMessage({ schemaType: SCHEMA_TYPES.response }, routeOptions))
78+
throw new Error(
79+
getErrorMessage({ schemaType: SCHEMA_TYPES.response }, routeOptions)
80+
)
7181
}
7282

73-
if (
74-
routeOptions?.schema?.response &&
75-
!responseKeys.length
76-
) {
77-
throw new Error(getErrorMessage({ message: 'No HTTP status codes provided in the response schema' }, routeOptions))
83+
if (routeOptions?.schema?.response && !responseKeys.length) {
84+
throw new Error(
85+
getErrorMessage(
86+
{
87+
message:
88+
'no HTTP status codes were provided in the response schema'
89+
},
90+
routeOptions
91+
)
92+
)
7893
}
7994

8095
responseKeys.forEach((value) => {
81-
if (value === 'default' || ['1xx', '2xx', '3xx', '4xx', '5xx'].includes(value) || (value >= 100 && value <= 599)) {
96+
if (
97+
value === 'default' ||
98+
['1xx', '2xx', '3xx', '4xx', '5xx'].includes(value) ||
99+
(value >= 100 && value <= 599)
100+
) {
82101
if (hasProperties(routeOptions, SCHEMA_TYPES.response, value)) {
83102
done()
84103
return
85104
}
86105

87-
throw new Error(getErrorMessage({ message: `${SCHEMA_TYPES.response} key "${value}" must be a non-empty object` }, routeOptions))
106+
throw new Error(
107+
getErrorMessage(
108+
{
109+
message: `schema ${SCHEMA_TYPES.response} key "${value}" must be a non-empty object`
110+
},
111+
routeOptions
112+
)
113+
)
88114
}
89115

90116
if (Number.isInteger(parseInt(value, 10))) {
91-
throw new Error(getErrorMessage({ message: 'valid HTTP status codes range from 100 - 599' }, routeOptions))
117+
throw new Error(
118+
getErrorMessage(
119+
{
120+
message: `schema ${SCHEMA_TYPES.response} key "${value}" must be a valid HTTP code which ranges from 100 - 599`
121+
},
122+
routeOptions
123+
)
124+
)
92125
}
93126

94-
throw new Error(getErrorMessage({ message: `"${value}" is not "default" or a supported HTTP status code` }, routeOptions))
127+
throw new Error(
128+
getErrorMessage(
129+
{
130+
message: `"${value}" is not \`default\` or a supported HTTP status code`
131+
},
132+
routeOptions
133+
)
134+
)
95135
})
96136
}
137+
138+
// NOTE: Deprecation will be removed in the next version
139+
if (routeOptions?.schema?.body === false) {
140+
process.emitWarning(
141+
`[${ErrorPrefix}] Setting "schema.body" to false is deprecated. Please use the "schema.disabled" option instead.`,
142+
'DeprecationWarning'
143+
)
144+
if (Array.isArray(routeOptions.schema.disabled)) {
145+
if (routeOptions.schema.disabled.indexOf(SCHEMA_TYPES.body) === -1) {
146+
routeOptions.schema.disabled.push(SCHEMA_TYPES.body)
147+
}
148+
} else {
149+
routeOptions.schema.disabled = [SCHEMA_TYPES.body]
150+
}
151+
delete routeOptions?.schema.body
152+
}
153+
97154
if (
98-
routeOptions?.schema?.body !== false &&
155+
!routeOptions?.schema?.disabled?.includes(SCHEMA_TYPES.body) &&
99156
!isSchemaTypeExcluded(excludedEntity, SCHEMA_TYPES.body) &&
100157
['POST', 'PUT', 'PATCH'].includes(routeOptions.method) &&
101158
disabled.indexOf(SCHEMA_TYPES.body) === -1 &&
102159
!hasProperties(routeOptions, SCHEMA_TYPES.body)
103160
) {
104-
throw new Error(getErrorMessage({ schemaType: SCHEMA_TYPES.body }, routeOptions))
161+
throw new Error(
162+
getErrorMessage({ schemaType: SCHEMA_TYPES.body }, routeOptions)
163+
)
164+
}
165+
166+
// NOTE: Deprecation will be removed in the next version
167+
if (routeOptions?.schema?.params === false) {
168+
process.emitWarning(
169+
`[${ErrorPrefix}] Setting "schema.params" to false is deprecated. Please use the "schema.disabled" option instead.`,
170+
'DeprecationWarning'
171+
)
172+
if (Array.isArray(routeOptions.schema.disabled)) {
173+
if (routeOptions.schema.disabled.indexOf(SCHEMA_TYPES.params) === -1) {
174+
routeOptions.schema.disabled.push(SCHEMA_TYPES.params)
175+
}
176+
} else {
177+
routeOptions.schema.disabled = [SCHEMA_TYPES.params]
178+
}
179+
delete routeOptions?.schema.params
105180
}
106181

107182
if (
108-
routeOptions?.schema?.params !== false &&
183+
!routeOptions?.schema?.disabled?.includes(SCHEMA_TYPES.params) &&
109184
/:\w+/.test(routeOptions.url) &&
110185
!isSchemaTypeExcluded(excludedEntity, SCHEMA_TYPES.params) &&
111186
disabled.indexOf(SCHEMA_TYPES.params) === -1 &&
112187
!hasProperties(routeOptions, SCHEMA_TYPES.params)
113188
) {
114-
throw new Error(getErrorMessage({ schemaType: SCHEMA_TYPES.params }, routeOptions))
189+
throw new Error(
190+
getErrorMessage({ schemaType: SCHEMA_TYPES.params }, routeOptions)
191+
)
115192
}
116193
})
117194

package.json

+12-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
"name": "fastify-enforce-schema",
33
"url": "",
44
"author": "Aleksandar Grbic - (https://programmer.network)",
5-
"version": "1.0.9",
5+
"version": "1.1.0",
6+
"contributors": [
7+
"Kirill Tregubov <[email protected]> (https://kirilltregubov.com)"
8+
],
69
"description": "Enforce AJV schemas across your endpoints.",
710
"repository": {
811
"type": "git",
@@ -23,18 +26,18 @@
2326
},
2427
"license": "MIT",
2528
"dependencies": {
26-
"fastify-plugin": "^4.3.0"
29+
"fastify-plugin": "^4.5.0"
2730
},
2831
"devDependencies": {
2932
"@fastify/pre-commit": "^2.0.2",
30-
"@types/node": "^18.0.0",
31-
"@typescript-eslint/eslint-plugin": "^5.12.1",
32-
"@typescript-eslint/parser": "^5.12.1",
33-
"fastify": "^4.0.0-rc.2",
33+
"@types/node": "^20.1.0",
34+
"@typescript-eslint/eslint-plugin": "^5.59.2",
35+
"@typescript-eslint/parser": "^5.59.2",
36+
"fastify": "4.16.0",
3437
"standard": "^17.0.0",
35-
"tap": "^16.0.0",
36-
"tsd": "^0.24.1",
37-
"typescript": "^4.0.2"
38+
"tap": "^16.3.4",
39+
"tsd": "^0.28.1",
40+
"typescript": "^5.0.4"
3841
},
3942
"keywords": [
4043
"fastify",

0 commit comments

Comments
 (0)