node-fastify 5.8.3

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.
Files changed (354) hide show
  1. package/.borp.yaml +3 -0
  2. package/.markdownlint-cli2.yaml +22 -0
  3. package/.prettierignore +1 -0
  4. package/GOVERNANCE.md +4 -0
  5. package/LICENSE +21 -0
  6. package/PROJECT_CHARTER.md +126 -0
  7. package/README.md +423 -0
  8. package/SECURITY.md +220 -0
  9. package/SPONSORS.md +24 -0
  10. package/build/build-error-serializer.js +35 -0
  11. package/build/build-validation.js +169 -0
  12. package/build/sync-version.js +11 -0
  13. package/docs/Guides/Benchmarking.md +60 -0
  14. package/docs/Guides/Database.md +321 -0
  15. package/docs/Guides/Delay-Accepting-Requests.md +608 -0
  16. package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
  17. package/docs/Guides/Ecosystem.md +726 -0
  18. package/docs/Guides/Fluent-Schema.md +127 -0
  19. package/docs/Guides/Getting-Started.md +620 -0
  20. package/docs/Guides/Index.md +43 -0
  21. package/docs/Guides/Migration-Guide-V3.md +287 -0
  22. package/docs/Guides/Migration-Guide-V4.md +267 -0
  23. package/docs/Guides/Migration-Guide-V5.md +727 -0
  24. package/docs/Guides/Plugins-Guide.md +520 -0
  25. package/docs/Guides/Prototype-Poisoning.md +383 -0
  26. package/docs/Guides/Recommendations.md +378 -0
  27. package/docs/Guides/Serverless.md +604 -0
  28. package/docs/Guides/Style-Guide.md +246 -0
  29. package/docs/Guides/Testing.md +481 -0
  30. package/docs/Guides/Write-Plugin.md +103 -0
  31. package/docs/Guides/Write-Type-Provider.md +34 -0
  32. package/docs/Reference/ContentTypeParser.md +271 -0
  33. package/docs/Reference/Decorators.md +436 -0
  34. package/docs/Reference/Encapsulation.md +194 -0
  35. package/docs/Reference/Errors.md +377 -0
  36. package/docs/Reference/HTTP2.md +94 -0
  37. package/docs/Reference/Hooks.md +958 -0
  38. package/docs/Reference/Index.md +73 -0
  39. package/docs/Reference/LTS.md +86 -0
  40. package/docs/Reference/Lifecycle.md +99 -0
  41. package/docs/Reference/Logging.md +268 -0
  42. package/docs/Reference/Middleware.md +79 -0
  43. package/docs/Reference/Plugins.md +245 -0
  44. package/docs/Reference/Principles.md +73 -0
  45. package/docs/Reference/Reply.md +1001 -0
  46. package/docs/Reference/Request.md +295 -0
  47. package/docs/Reference/Routes.md +802 -0
  48. package/docs/Reference/Server.md +2389 -0
  49. package/docs/Reference/Type-Providers.md +256 -0
  50. package/docs/Reference/TypeScript.md +1729 -0
  51. package/docs/Reference/Validation-and-Serialization.md +1130 -0
  52. package/docs/Reference/Warnings.md +58 -0
  53. package/docs/index.md +24 -0
  54. package/docs/resources/encapsulation_context.drawio +1 -0
  55. package/docs/resources/encapsulation_context.svg +3 -0
  56. package/eslint.config.js +35 -0
  57. package/examples/asyncawait.js +38 -0
  58. package/examples/benchmark/body.json +3 -0
  59. package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
  60. package/examples/benchmark/hooks-benchmark.js +52 -0
  61. package/examples/benchmark/parser.js +47 -0
  62. package/examples/benchmark/simple.js +30 -0
  63. package/examples/benchmark/webstream.js +27 -0
  64. package/examples/hooks.js +91 -0
  65. package/examples/http2.js +39 -0
  66. package/examples/https.js +38 -0
  67. package/examples/parser.js +53 -0
  68. package/examples/plugin.js +12 -0
  69. package/examples/route-prefix.js +38 -0
  70. package/examples/shared-schema.js +38 -0
  71. package/examples/simple-stream.js +20 -0
  72. package/examples/simple.js +32 -0
  73. package/examples/simple.mjs +27 -0
  74. package/examples/typescript-server.ts +79 -0
  75. package/examples/use-plugin.js +29 -0
  76. package/fastify.d.ts +253 -0
  77. package/fastify.js +985 -0
  78. package/integration/server.js +29 -0
  79. package/integration/test.sh +23 -0
  80. package/lib/config-validator.js +1266 -0
  81. package/lib/content-type-parser.js +413 -0
  82. package/lib/content-type.js +160 -0
  83. package/lib/context.js +98 -0
  84. package/lib/decorate.js +152 -0
  85. package/lib/error-handler.js +173 -0
  86. package/lib/error-serializer.js +134 -0
  87. package/lib/error-status.js +14 -0
  88. package/lib/errors.js +516 -0
  89. package/lib/four-oh-four.js +190 -0
  90. package/lib/handle-request.js +195 -0
  91. package/lib/head-route.js +45 -0
  92. package/lib/hooks.js +429 -0
  93. package/lib/initial-config-validation.js +37 -0
  94. package/lib/logger-factory.js +136 -0
  95. package/lib/logger-pino.js +68 -0
  96. package/lib/noop-set.js +10 -0
  97. package/lib/plugin-override.js +90 -0
  98. package/lib/plugin-utils.js +169 -0
  99. package/lib/promise.js +23 -0
  100. package/lib/reply.js +1030 -0
  101. package/lib/req-id-gen-factory.js +52 -0
  102. package/lib/request.js +391 -0
  103. package/lib/route.js +686 -0
  104. package/lib/schema-controller.js +164 -0
  105. package/lib/schemas.js +207 -0
  106. package/lib/server.js +441 -0
  107. package/lib/symbols.js +71 -0
  108. package/lib/validation.js +280 -0
  109. package/lib/warnings.js +57 -0
  110. package/lib/wrap-thenable.js +84 -0
  111. package/package.json +225 -0
  112. package/scripts/validate-ecosystem-links.js +179 -0
  113. package/test/404s.test.js +2035 -0
  114. package/test/500s.test.js +422 -0
  115. package/test/allow-unsafe-regex.test.js +92 -0
  116. package/test/als.test.js +65 -0
  117. package/test/async-await.test.js +705 -0
  118. package/test/async-dispose.test.js +20 -0
  119. package/test/async_hooks.test.js +52 -0
  120. package/test/body-limit.test.js +224 -0
  121. package/test/buffer.test.js +74 -0
  122. package/test/build/error-serializer.test.js +36 -0
  123. package/test/build/version.test.js +14 -0
  124. package/test/build-certificate.js +109 -0
  125. package/test/bundler/README.md +29 -0
  126. package/test/bundler/esbuild/bundler-test.js +32 -0
  127. package/test/bundler/esbuild/package.json +10 -0
  128. package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
  129. package/test/bundler/esbuild/src/index.js +9 -0
  130. package/test/bundler/webpack/bundler-test.js +32 -0
  131. package/test/bundler/webpack/package.json +11 -0
  132. package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
  133. package/test/bundler/webpack/src/index.js +9 -0
  134. package/test/bundler/webpack/webpack.config.js +15 -0
  135. package/test/case-insensitive.test.js +102 -0
  136. package/test/chainable.test.js +40 -0
  137. package/test/child-logger-factory.test.js +128 -0
  138. package/test/client-timeout.test.js +38 -0
  139. package/test/close-pipelining.test.js +78 -0
  140. package/test/close.test.js +706 -0
  141. package/test/conditional-pino.test.js +47 -0
  142. package/test/connection-timeout.test.js +42 -0
  143. package/test/constrained-routes.test.js +1138 -0
  144. package/test/content-length.test.js +174 -0
  145. package/test/content-parser.test.js +739 -0
  146. package/test/content-type.test.js +181 -0
  147. package/test/context-config.test.js +164 -0
  148. package/test/custom-http-server.test.js +118 -0
  149. package/test/custom-parser-async.test.js +59 -0
  150. package/test/custom-parser.0.test.js +701 -0
  151. package/test/custom-parser.1.test.js +266 -0
  152. package/test/custom-parser.2.test.js +91 -0
  153. package/test/custom-parser.3.test.js +208 -0
  154. package/test/custom-parser.4.test.js +218 -0
  155. package/test/custom-parser.5.test.js +130 -0
  156. package/test/custom-querystring-parser.test.js +129 -0
  157. package/test/decorator.test.js +1330 -0
  158. package/test/delete.test.js +344 -0
  159. package/test/diagnostics-channel/404.test.js +49 -0
  160. package/test/diagnostics-channel/async-delay-request.test.js +65 -0
  161. package/test/diagnostics-channel/async-request.test.js +64 -0
  162. package/test/diagnostics-channel/error-before-handler.test.js +35 -0
  163. package/test/diagnostics-channel/error-request.test.js +53 -0
  164. package/test/diagnostics-channel/error-status.test.js +123 -0
  165. package/test/diagnostics-channel/init.test.js +50 -0
  166. package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
  167. package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
  168. package/test/diagnostics-channel/sync-request.test.js +54 -0
  169. package/test/encapsulated-child-logger-factory.test.js +69 -0
  170. package/test/encapsulated-error-handler.test.js +237 -0
  171. package/test/esm/errorCodes.test.mjs +10 -0
  172. package/test/esm/esm.test.mjs +13 -0
  173. package/test/esm/index.test.js +8 -0
  174. package/test/esm/named-exports.mjs +14 -0
  175. package/test/esm/other.mjs +8 -0
  176. package/test/esm/plugin.mjs +8 -0
  177. package/test/fastify-instance.test.js +300 -0
  178. package/test/find-route.test.js +152 -0
  179. package/test/fluent-schema.test.js +209 -0
  180. package/test/genReqId.test.js +426 -0
  181. package/test/handler-context.test.js +45 -0
  182. package/test/handler-timeout.test.js +367 -0
  183. package/test/has-route.test.js +88 -0
  184. package/test/header-overflow.test.js +55 -0
  185. package/test/helper.js +496 -0
  186. package/test/hooks-async.test.js +1099 -0
  187. package/test/hooks.on-listen.test.js +1162 -0
  188. package/test/hooks.on-ready.test.js +421 -0
  189. package/test/hooks.test.js +3578 -0
  190. package/test/http-methods/copy.test.js +35 -0
  191. package/test/http-methods/custom-http-methods.test.js +114 -0
  192. package/test/http-methods/get.test.js +412 -0
  193. package/test/http-methods/head.test.js +263 -0
  194. package/test/http-methods/lock.test.js +108 -0
  195. package/test/http-methods/mkcalendar.test.js +143 -0
  196. package/test/http-methods/mkcol.test.js +35 -0
  197. package/test/http-methods/move.test.js +42 -0
  198. package/test/http-methods/propfind.test.js +136 -0
  199. package/test/http-methods/proppatch.test.js +105 -0
  200. package/test/http-methods/report.test.js +142 -0
  201. package/test/http-methods/search.test.js +233 -0
  202. package/test/http-methods/trace.test.js +21 -0
  203. package/test/http-methods/unlock.test.js +38 -0
  204. package/test/http2/closing.test.js +270 -0
  205. package/test/http2/constraint.test.js +109 -0
  206. package/test/http2/head.test.js +34 -0
  207. package/test/http2/plain.test.js +68 -0
  208. package/test/http2/secure-with-fallback.test.js +113 -0
  209. package/test/http2/secure.test.js +67 -0
  210. package/test/http2/unknown-http-method.test.js +34 -0
  211. package/test/https/custom-https-server.test.js +58 -0
  212. package/test/https/https.test.js +136 -0
  213. package/test/imports.test.js +17 -0
  214. package/test/inject.test.js +502 -0
  215. package/test/input-validation.js +335 -0
  216. package/test/internals/all.test.js +38 -0
  217. package/test/internals/content-type-parser.test.js +111 -0
  218. package/test/internals/context.test.js +31 -0
  219. package/test/internals/decorator.test.js +156 -0
  220. package/test/internals/errors.test.js +982 -0
  221. package/test/internals/handle-request.test.js +270 -0
  222. package/test/internals/hook-runner.test.js +449 -0
  223. package/test/internals/hooks.test.js +96 -0
  224. package/test/internals/initial-config.test.js +383 -0
  225. package/test/internals/logger.test.js +163 -0
  226. package/test/internals/plugin.test.js +170 -0
  227. package/test/internals/promise.test.js +63 -0
  228. package/test/internals/reply-serialize.test.js +714 -0
  229. package/test/internals/reply.test.js +1920 -0
  230. package/test/internals/req-id-gen-factory.test.js +133 -0
  231. package/test/internals/request-validate.test.js +1402 -0
  232. package/test/internals/request.test.js +506 -0
  233. package/test/internals/schema-controller-perf.test.js +40 -0
  234. package/test/internals/server.test.js +91 -0
  235. package/test/internals/validation.test.js +352 -0
  236. package/test/issue-4959.test.js +118 -0
  237. package/test/keep-alive-timeout.test.js +42 -0
  238. package/test/listen.1.test.js +154 -0
  239. package/test/listen.2.test.js +113 -0
  240. package/test/listen.3.test.js +83 -0
  241. package/test/listen.4.test.js +168 -0
  242. package/test/listen.5.test.js +122 -0
  243. package/test/logger/instantiation.test.js +341 -0
  244. package/test/logger/logger-test-utils.js +47 -0
  245. package/test/logger/logging.test.js +460 -0
  246. package/test/logger/options.test.js +579 -0
  247. package/test/logger/request.test.js +292 -0
  248. package/test/logger/response.test.js +183 -0
  249. package/test/logger/tap-parallel-not-ok +0 -0
  250. package/test/max-requests-per-socket.test.js +113 -0
  251. package/test/middleware.test.js +37 -0
  252. package/test/noop-set.test.js +19 -0
  253. package/test/nullable-validation.test.js +187 -0
  254. package/test/options.error-handler.test.js +5 -0
  255. package/test/options.test.js +5 -0
  256. package/test/output-validation.test.js +140 -0
  257. package/test/patch.error-handler.test.js +5 -0
  258. package/test/patch.test.js +5 -0
  259. package/test/plugin.1.test.js +230 -0
  260. package/test/plugin.2.test.js +314 -0
  261. package/test/plugin.3.test.js +287 -0
  262. package/test/plugin.4.test.js +504 -0
  263. package/test/plugin.helper.js +8 -0
  264. package/test/plugin.name.display.js +10 -0
  265. package/test/post-empty-body.test.js +38 -0
  266. package/test/pretty-print.test.js +366 -0
  267. package/test/promises.test.js +125 -0
  268. package/test/proto-poisoning.test.js +145 -0
  269. package/test/put.error-handler.test.js +5 -0
  270. package/test/put.test.js +5 -0
  271. package/test/register.test.js +184 -0
  272. package/test/reply-code.test.js +148 -0
  273. package/test/reply-early-hints.test.js +100 -0
  274. package/test/reply-error.test.js +815 -0
  275. package/test/reply-trailers.test.js +445 -0
  276. package/test/reply-web-stream-locked.test.js +37 -0
  277. package/test/request-error.test.js +624 -0
  278. package/test/request-header-host.test.js +339 -0
  279. package/test/request-id.test.js +118 -0
  280. package/test/request-timeout.test.js +53 -0
  281. package/test/route-hooks.test.js +635 -0
  282. package/test/route-prefix.test.js +904 -0
  283. package/test/route-shorthand.test.js +48 -0
  284. package/test/route.1.test.js +259 -0
  285. package/test/route.2.test.js +100 -0
  286. package/test/route.3.test.js +213 -0
  287. package/test/route.4.test.js +127 -0
  288. package/test/route.5.test.js +211 -0
  289. package/test/route.6.test.js +306 -0
  290. package/test/route.7.test.js +406 -0
  291. package/test/route.8.test.js +225 -0
  292. package/test/router-options.test.js +1108 -0
  293. package/test/same-shape.test.js +124 -0
  294. package/test/schema-examples.test.js +661 -0
  295. package/test/schema-feature.test.js +2198 -0
  296. package/test/schema-serialization.test.js +1171 -0
  297. package/test/schema-special-usage.test.js +1348 -0
  298. package/test/schema-validation.test.js +1572 -0
  299. package/test/scripts/validate-ecosystem-links.test.js +339 -0
  300. package/test/serialize-response.test.js +186 -0
  301. package/test/server.test.js +347 -0
  302. package/test/set-error-handler.test.js +69 -0
  303. package/test/skip-reply-send.test.js +317 -0
  304. package/test/stream-serializers.test.js +40 -0
  305. package/test/stream.1.test.js +94 -0
  306. package/test/stream.2.test.js +129 -0
  307. package/test/stream.3.test.js +198 -0
  308. package/test/stream.4.test.js +176 -0
  309. package/test/stream.5.test.js +188 -0
  310. package/test/sync-routes.test.js +32 -0
  311. package/test/throw.test.js +359 -0
  312. package/test/toolkit.js +63 -0
  313. package/test/trust-proxy.test.js +162 -0
  314. package/test/type-provider.test.js +22 -0
  315. package/test/types/content-type-parser.test-d.ts +72 -0
  316. package/test/types/decorate-request-reply.test-d.ts +18 -0
  317. package/test/types/dummy-plugin.ts +9 -0
  318. package/test/types/errors.test-d.ts +90 -0
  319. package/test/types/fastify.test-d.ts +352 -0
  320. package/test/types/hooks.test-d.ts +550 -0
  321. package/test/types/import.ts +2 -0
  322. package/test/types/instance.test-d.ts +588 -0
  323. package/test/types/logger.test-d.ts +277 -0
  324. package/test/types/plugin.test-d.ts +97 -0
  325. package/test/types/register.test-d.ts +237 -0
  326. package/test/types/reply.test-d.ts +254 -0
  327. package/test/types/request.test-d.ts +188 -0
  328. package/test/types/route.test-d.ts +553 -0
  329. package/test/types/schema.test-d.ts +135 -0
  330. package/test/types/serverFactory.test-d.ts +37 -0
  331. package/test/types/type-provider.test-d.ts +1213 -0
  332. package/test/types/using.test-d.ts +17 -0
  333. package/test/upgrade.test.js +52 -0
  334. package/test/url-rewriting.test.js +122 -0
  335. package/test/use-semicolon-delimiter.test.js +168 -0
  336. package/test/validation-error-handling.test.js +900 -0
  337. package/test/versioned-routes.test.js +603 -0
  338. package/test/web-api.test.js +616 -0
  339. package/test/wrap-thenable.test.js +30 -0
  340. package/types/content-type-parser.d.ts +75 -0
  341. package/types/context.d.ts +22 -0
  342. package/types/errors.d.ts +92 -0
  343. package/types/hooks.d.ts +875 -0
  344. package/types/instance.d.ts +609 -0
  345. package/types/logger.d.ts +107 -0
  346. package/types/plugin.d.ts +44 -0
  347. package/types/register.d.ts +42 -0
  348. package/types/reply.d.ts +81 -0
  349. package/types/request.d.ts +95 -0
  350. package/types/route.d.ts +199 -0
  351. package/types/schema.d.ts +61 -0
  352. package/types/server-factory.d.ts +19 -0
  353. package/types/type-provider.d.ts +130 -0
  354. package/types/utils.d.ts +98 -0
@@ -0,0 +1,48 @@
1
+ 'use strict'
2
+
3
+ const { describe, test } = require('node:test')
4
+ const { Client } = require('undici')
5
+ const Fastify = require('..')
6
+
7
+ describe('route-shorthand', () => {
8
+ const methodsReader = new Fastify()
9
+ const supportedMethods = methodsReader.supportedMethods
10
+
11
+ for (const method of supportedMethods) {
12
+ test(`route-shorthand - ${method.toLowerCase()}`, async (t) => {
13
+ t.plan(2)
14
+ const fastify = new Fastify()
15
+ fastify[method.toLowerCase()]('/', (req, reply) => {
16
+ t.assert.strictEqual(req.method, method)
17
+ reply.send()
18
+ })
19
+ await fastify.listen({ port: 0 })
20
+ t.after(() => fastify.close())
21
+
22
+ const instance = new Client(`http://localhost:${fastify.server.address().port}`)
23
+
24
+ const response = await instance.request({ path: '/', method })
25
+ t.assert.strictEqual(response.statusCode, 200)
26
+ })
27
+ }
28
+
29
+ test('route-shorthand - all', async (t) => {
30
+ t.plan(2 * supportedMethods.length)
31
+ const fastify = new Fastify()
32
+ let currentMethod = ''
33
+ fastify.all('/', function (req, reply) {
34
+ t.assert.strictEqual(req.method, currentMethod)
35
+ reply.send()
36
+ })
37
+ await fastify.listen({ port: 0 })
38
+ t.after(() => fastify.close())
39
+
40
+ for (const method of supportedMethods) {
41
+ currentMethod = method
42
+ const instance = new Client(`http://localhost:${fastify.server.address().port}`)
43
+
44
+ const response = await instance.request({ path: '/', method })
45
+ t.assert.strictEqual(response.statusCode, 200)
46
+ }
47
+ })
48
+ })
@@ -0,0 +1,259 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('..')
5
+ const {
6
+ FST_ERR_INSTANCE_ALREADY_LISTENING,
7
+ FST_ERR_ROUTE_METHOD_INVALID
8
+ } = require('../lib/errors')
9
+ const { getServerUrl } = require('./helper')
10
+
11
+ test('route', async t => {
12
+ t.plan(10)
13
+
14
+ await t.test('route - get', async (t) => {
15
+ t.plan(4)
16
+
17
+ const fastify = Fastify()
18
+ t.assert.doesNotThrow(() =>
19
+ fastify.route({
20
+ method: 'GET',
21
+ url: '/',
22
+ schema: {
23
+ response: {
24
+ '2xx': {
25
+ type: 'object',
26
+ properties: {
27
+ hello: {
28
+ type: 'string'
29
+ }
30
+ }
31
+ }
32
+ }
33
+ },
34
+ handler: function (req, reply) {
35
+ reply.send({ hello: 'world' })
36
+ }
37
+ })
38
+ )
39
+
40
+ await fastify.listen({ port: 0 })
41
+ t.after(() => { fastify.close() })
42
+
43
+ const response = await fetch(getServerUrl(fastify) + '/')
44
+ t.assert.ok(response.ok)
45
+ t.assert.strictEqual(response.status, 200)
46
+ t.assert.deepStrictEqual(await response.json(), { hello: 'world' })
47
+ })
48
+
49
+ await t.test('missing schema - route', async (t) => {
50
+ t.plan(4)
51
+
52
+ const fastify = Fastify()
53
+ t.assert.doesNotThrow(() =>
54
+ fastify.route({
55
+ method: 'GET',
56
+ url: '/missing',
57
+ handler: function (req, reply) {
58
+ reply.send({ hello: 'world' })
59
+ }
60
+ })
61
+ )
62
+
63
+ await fastify.listen({ port: 0 })
64
+ t.after(() => { fastify.close() })
65
+
66
+ const response = await fetch(getServerUrl(fastify) + '/missing')
67
+ t.assert.ok(response.ok)
68
+ t.assert.strictEqual(response.status, 200)
69
+ t.assert.deepStrictEqual(await response.json(), { hello: 'world' })
70
+ })
71
+
72
+ await t.test('invalid handler attribute - route', t => {
73
+ t.plan(1)
74
+
75
+ const fastify = Fastify()
76
+ t.assert.throws(() => fastify.get('/', { handler: 'not a function' }, () => { }))
77
+ })
78
+
79
+ await t.test('Add Multiple methods per route all uppercase', async (t) => {
80
+ t.plan(7)
81
+
82
+ const fastify = Fastify()
83
+ t.assert.doesNotThrow(() =>
84
+ fastify.route({
85
+ method: ['GET', 'DELETE'],
86
+ url: '/multiple',
87
+ handler: function (req, reply) {
88
+ reply.send({ hello: 'world' })
89
+ }
90
+ }))
91
+
92
+ await fastify.listen({ port: 0 })
93
+ t.after(() => { fastify.close() })
94
+
95
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
96
+ t.assert.ok(getResponse.ok)
97
+ t.assert.strictEqual(getResponse.status, 200)
98
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
99
+
100
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
101
+ t.assert.ok(deleteResponse.ok)
102
+ t.assert.strictEqual(deleteResponse.status, 200)
103
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
104
+ })
105
+
106
+ await t.test('Add Multiple methods per route all lowercase', async (t) => {
107
+ t.plan(7)
108
+
109
+ const fastify = Fastify()
110
+ t.assert.doesNotThrow(() =>
111
+ fastify.route({
112
+ method: ['get', 'delete'],
113
+ url: '/multiple',
114
+ handler: function (req, reply) {
115
+ reply.send({ hello: 'world' })
116
+ }
117
+ }))
118
+
119
+ await fastify.listen({ port: 0 })
120
+ t.after(() => { fastify.close() })
121
+
122
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
123
+ t.assert.ok(getResponse.ok)
124
+ t.assert.strictEqual(getResponse.status, 200)
125
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
126
+
127
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
128
+ t.assert.ok(deleteResponse.ok)
129
+ t.assert.strictEqual(deleteResponse.status, 200)
130
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
131
+ })
132
+
133
+ await t.test('Add Multiple methods per route mixed uppercase and lowercase', async (t) => {
134
+ t.plan(7)
135
+
136
+ const fastify = Fastify()
137
+ t.assert.doesNotThrow(() =>
138
+ fastify.route({
139
+ method: ['GET', 'delete'],
140
+ url: '/multiple',
141
+ handler: function (req, reply) {
142
+ reply.send({ hello: 'world' })
143
+ }
144
+ }))
145
+
146
+ await fastify.listen({ port: 0 })
147
+ t.after(() => { fastify.close() })
148
+
149
+ const getResponse = await fetch(getServerUrl(fastify) + '/multiple')
150
+ t.assert.ok(getResponse.ok)
151
+ t.assert.strictEqual(getResponse.status, 200)
152
+ t.assert.deepStrictEqual(await getResponse.json(), { hello: 'world' })
153
+
154
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/multiple', { method: 'DELETE' })
155
+ t.assert.ok(deleteResponse.ok)
156
+ t.assert.strictEqual(deleteResponse.status, 200)
157
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
158
+ })
159
+
160
+ t.test('Add invalid Multiple methods per route', t => {
161
+ t.plan(1)
162
+
163
+ const fastify = Fastify()
164
+ t.assert.throws(() =>
165
+ fastify.route({
166
+ method: ['GET', 1],
167
+ url: '/invalid-method',
168
+ handler: function (req, reply) {
169
+ reply.send({ hello: 'world' })
170
+ }
171
+ }), new FST_ERR_ROUTE_METHOD_INVALID())
172
+ })
173
+
174
+ await t.test('Add method', t => {
175
+ t.plan(1)
176
+
177
+ const fastify = Fastify()
178
+ t.assert.throws(() =>
179
+ fastify.route({
180
+ method: 1,
181
+ url: '/invalid-method',
182
+ handler: function (req, reply) {
183
+ reply.send({ hello: 'world' })
184
+ }
185
+ }), new FST_ERR_ROUTE_METHOD_INVALID())
186
+ })
187
+
188
+ await t.test('Add additional multiple methods to existing route', async (t) => {
189
+ t.plan(7)
190
+
191
+ const fastify = Fastify()
192
+ t.assert.doesNotThrow(() => {
193
+ fastify.get('/add-multiple', function (req, reply) {
194
+ reply.send({ hello: 'Bob!' })
195
+ })
196
+ fastify.route({
197
+ method: ['PUT', 'DELETE'],
198
+ url: '/add-multiple',
199
+ handler: function (req, reply) {
200
+ reply.send({ hello: 'world' })
201
+ }
202
+ })
203
+ })
204
+
205
+ await fastify.listen({ port: 0 })
206
+ t.after(() => { fastify.close() })
207
+
208
+ const putResponse = await fetch(getServerUrl(fastify) + '/add-multiple', { method: 'PUT' })
209
+ t.assert.ok(putResponse.ok)
210
+ t.assert.strictEqual(putResponse.status, 200)
211
+ t.assert.deepStrictEqual(await putResponse.json(), { hello: 'world' })
212
+
213
+ const deleteResponse = await fetch(getServerUrl(fastify) + '/add-multiple', { method: 'DELETE' })
214
+ t.assert.ok(deleteResponse.ok)
215
+ t.assert.strictEqual(deleteResponse.status, 200)
216
+ t.assert.deepStrictEqual(await deleteResponse.json(), { hello: 'world' })
217
+ })
218
+
219
+ await t.test('cannot add another route after binding', async (t) => {
220
+ t.plan(1)
221
+
222
+ const fastify = Fastify()
223
+
224
+ await fastify.listen({ port: 0 })
225
+ t.after(() => { fastify.close() })
226
+
227
+ t.assert.throws(() => fastify.route({
228
+ method: 'GET',
229
+ url: '/another-get-route',
230
+ handler: function (req, reply) {
231
+ reply.send({ hello: 'world' })
232
+ }
233
+ }), new FST_ERR_INSTANCE_ALREADY_LISTENING('Cannot add route!'))
234
+ })
235
+ })
236
+
237
+ test('invalid schema - route', (t, done) => {
238
+ t.plan(3)
239
+
240
+ const fastify = Fastify()
241
+ fastify.route({
242
+ handler: () => { },
243
+ method: 'GET',
244
+ url: '/invalid',
245
+ schema: {
246
+ querystring: {
247
+ id: 'string'
248
+ }
249
+ }
250
+ })
251
+ fastify.after(err => {
252
+ t.assert.ok(!err, 'the error is throw on preReady')
253
+ })
254
+ fastify.ready(err => {
255
+ t.assert.strictEqual(err.code, 'FST_ERR_SCH_VALIDATION_BUILD')
256
+ t.assert.match(err.message, /Failed building the validation schema for GET: \/invalid/)
257
+ done()
258
+ })
259
+ })
@@ -0,0 +1,100 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const Fastify = require('../fastify')
5
+
6
+ test('same route definition object on multiple prefixes', async t => {
7
+ t.plan(2)
8
+
9
+ const routeObject = {
10
+ handler: () => { },
11
+ method: 'GET',
12
+ url: '/simple'
13
+ }
14
+
15
+ const fastify = Fastify({ exposeHeadRoutes: false })
16
+
17
+ fastify.register(async function (f) {
18
+ f.addHook('onRoute', (routeOptions) => {
19
+ t.assert.strictEqual(routeOptions.url, '/v1/simple')
20
+ })
21
+ f.route(routeObject)
22
+ }, { prefix: '/v1' })
23
+ fastify.register(async function (f) {
24
+ f.addHook('onRoute', (routeOptions) => {
25
+ t.assert.strictEqual(routeOptions.url, '/v2/simple')
26
+ })
27
+ f.route(routeObject)
28
+ }, { prefix: '/v2' })
29
+
30
+ await fastify.ready()
31
+ })
32
+
33
+ test('path can be specified in place of uri', (t, done) => {
34
+ t.plan(3)
35
+ const fastify = Fastify()
36
+
37
+ fastify.route({
38
+ method: 'GET',
39
+ path: '/path',
40
+ handler: function (req, reply) {
41
+ reply.send({ hello: 'world' })
42
+ }
43
+ })
44
+
45
+ const reqOpts = {
46
+ method: 'GET',
47
+ url: '/path'
48
+ }
49
+
50
+ fastify.inject(reqOpts, (err, res) => {
51
+ t.assert.ifError(err)
52
+ t.assert.strictEqual(res.statusCode, 200)
53
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'world' })
54
+ done()
55
+ })
56
+ })
57
+
58
+ test('invalid bodyLimit option - route', t => {
59
+ t.plan(2)
60
+ const fastify = Fastify()
61
+
62
+ try {
63
+ fastify.route({
64
+ bodyLimit: false,
65
+ method: 'PUT',
66
+ handler: () => null
67
+ })
68
+ t.assert.fail('bodyLimit must be an integer')
69
+ } catch (err) {
70
+ t.assert.strictEqual(err.message, "'bodyLimit' option must be an integer > 0. Got 'false'")
71
+ }
72
+
73
+ try {
74
+ fastify.post('/url', { bodyLimit: 10000.1 }, () => null)
75
+ t.assert.fail('bodyLimit must be an integer')
76
+ } catch (err) {
77
+ t.assert.strictEqual(err.message, "'bodyLimit' option must be an integer > 0. Got '10000.1'")
78
+ }
79
+ })
80
+
81
+ test('handler function in options of shorthand route should works correctly', (t, done) => {
82
+ t.plan(3)
83
+
84
+ const fastify = Fastify()
85
+ fastify.get('/foo', {
86
+ handler: (req, reply) => {
87
+ reply.send({ hello: 'world' })
88
+ }
89
+ })
90
+
91
+ fastify.inject({
92
+ method: 'GET',
93
+ url: '/foo'
94
+ }, (err, res) => {
95
+ t.assert.ifError(err)
96
+ t.assert.strictEqual(res.statusCode, 200)
97
+ t.assert.deepStrictEqual(JSON.parse(res.payload), { hello: 'world' })
98
+ done()
99
+ })
100
+ })
@@ -0,0 +1,213 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('node:test')
4
+ const joi = require('joi')
5
+ const Fastify = require('..')
6
+
7
+ test('does not mutate joi schemas', (t, done) => {
8
+ t.plan(5)
9
+
10
+ const fastify = Fastify()
11
+ function validatorCompiler ({ schema, method, url, httpPart }) {
12
+ // Needed to extract the params part,
13
+ // without the JSON-schema encapsulation
14
+ // that is automatically added by the short
15
+ // form of params.
16
+ schema = joi.object(schema.properties)
17
+
18
+ return validateHttpData
19
+
20
+ function validateHttpData (data) {
21
+ return schema.validate(data)
22
+ }
23
+ }
24
+
25
+ fastify.setValidatorCompiler(validatorCompiler)
26
+
27
+ fastify.route({
28
+ path: '/foo/:an_id',
29
+ method: 'GET',
30
+ schema: {
31
+ params: { an_id: joi.number() }
32
+ },
33
+ handler (req, res) {
34
+ t.assert.strictEqual(Object.keys(req.params).length, 1)
35
+ t.assert.strictEqual(req.params.an_id, '42')
36
+ res.send({ hello: 'world' })
37
+ }
38
+ })
39
+
40
+ fastify.inject({
41
+ method: 'GET',
42
+ url: '/foo/42'
43
+ }, (err, res) => {
44
+ t.assert.ifError(err)
45
+ t.assert.strictEqual(res.statusCode, 200)
46
+ t.assert.deepStrictEqual(res.json(), { hello: 'world' })
47
+ done()
48
+ })
49
+ })
50
+
51
+ test('multiple routes with one schema', (t, done) => {
52
+ t.plan(2)
53
+
54
+ const fastify = Fastify()
55
+
56
+ const schema = {
57
+ query: {
58
+ type: 'object',
59
+ properties: {
60
+ id: { type: 'number' }
61
+ }
62
+ }
63
+ }
64
+
65
+ fastify.route({
66
+ schema,
67
+ method: 'GET',
68
+ path: '/first/:id',
69
+ handler (req, res) {
70
+ res.send({ hello: 'world' })
71
+ }
72
+ })
73
+
74
+ fastify.route({
75
+ schema,
76
+ method: 'GET',
77
+ path: '/second/:id',
78
+ handler (req, res) {
79
+ res.send({ hello: 'world' })
80
+ }
81
+ })
82
+
83
+ fastify.ready(error => {
84
+ t.assert.ifError(error)
85
+ t.assert.deepStrictEqual(schema, schema)
86
+ done()
87
+ })
88
+ })
89
+
90
+ test('route error handler overrides default error handler', (t, done) => {
91
+ t.plan(4)
92
+
93
+ const fastify = Fastify()
94
+
95
+ const customRouteErrorHandler = (error, request, reply) => {
96
+ t.assert.strictEqual(error.message, 'Wrong Pot Error')
97
+
98
+ reply.code(418).send({
99
+ message: 'Make a brew',
100
+ statusCode: 418,
101
+ error: 'Wrong Pot Error'
102
+ })
103
+ }
104
+
105
+ fastify.route({
106
+ method: 'GET',
107
+ path: '/coffee',
108
+ handler: (req, res) => {
109
+ res.send(new Error('Wrong Pot Error'))
110
+ },
111
+ errorHandler: customRouteErrorHandler
112
+ })
113
+
114
+ fastify.inject({
115
+ method: 'GET',
116
+ url: '/coffee'
117
+ }, (error, res) => {
118
+ t.assert.ifError(error)
119
+ t.assert.strictEqual(res.statusCode, 418)
120
+ t.assert.deepStrictEqual(res.json(), {
121
+ message: 'Make a brew',
122
+ statusCode: 418,
123
+ error: 'Wrong Pot Error'
124
+ })
125
+ done()
126
+ })
127
+ })
128
+
129
+ test('route error handler does not affect other routes', (t, done) => {
130
+ t.plan(3)
131
+
132
+ const fastify = Fastify()
133
+
134
+ const customRouteErrorHandler = (error, request, reply) => {
135
+ t.assert.strictEqual(error.message, 'Wrong Pot Error')
136
+
137
+ reply.code(418).send({
138
+ message: 'Make a brew',
139
+ statusCode: 418,
140
+ error: 'Wrong Pot Error'
141
+ })
142
+ }
143
+
144
+ fastify.route({
145
+ method: 'GET',
146
+ path: '/coffee',
147
+ handler: (req, res) => {
148
+ res.send(new Error('Wrong Pot Error'))
149
+ },
150
+ errorHandler: customRouteErrorHandler
151
+ })
152
+
153
+ fastify.route({
154
+ method: 'GET',
155
+ path: '/tea',
156
+ handler: (req, res) => {
157
+ res.send(new Error('No tea today'))
158
+ }
159
+ })
160
+
161
+ fastify.inject({
162
+ method: 'GET',
163
+ url: '/tea'
164
+ }, (error, res) => {
165
+ t.assert.ifError(error)
166
+ t.assert.strictEqual(res.statusCode, 500)
167
+ t.assert.deepStrictEqual(res.json(), {
168
+ message: 'No tea today',
169
+ statusCode: 500,
170
+ error: 'Internal Server Error'
171
+ })
172
+ done()
173
+ })
174
+ })
175
+
176
+ test('async error handler for a route', (t, done) => {
177
+ t.plan(4)
178
+
179
+ const fastify = Fastify()
180
+
181
+ const customRouteErrorHandler = async (error, request, reply) => {
182
+ t.assert.strictEqual(error.message, 'Delayed Pot Error')
183
+ reply.code(418)
184
+ return {
185
+ message: 'Make a brew sometime later',
186
+ statusCode: 418,
187
+ error: 'Delayed Pot Error'
188
+ }
189
+ }
190
+
191
+ fastify.route({
192
+ method: 'GET',
193
+ path: '/late-coffee',
194
+ handler: (req, res) => {
195
+ res.send(new Error('Delayed Pot Error'))
196
+ },
197
+ errorHandler: customRouteErrorHandler
198
+ })
199
+
200
+ fastify.inject({
201
+ method: 'GET',
202
+ url: '/late-coffee'
203
+ }, (error, res) => {
204
+ t.assert.ifError(error)
205
+ t.assert.strictEqual(res.statusCode, 418)
206
+ t.assert.deepStrictEqual(res.json(), {
207
+ message: 'Make a brew sometime later',
208
+ statusCode: 418,
209
+ error: 'Delayed Pot Error'
210
+ })
211
+ done()
212
+ })
213
+ })