@athenna/http 1.7.7 → 1.7.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@athenna/http",
3
- "version": "1.7.7",
3
+ "version": "1.7.9",
4
4
  "description": "The Athenna Http server. Built on top of fastify.",
5
5
  "license": "MIT",
6
6
  "author": "João Lenon <lenon@athenna.io>",
@@ -54,14 +54,17 @@
54
54
  "#tests/*": "./tests/*.js"
55
55
  },
56
56
  "dependencies": {
57
- "@athenna/artisan": "1.5.8",
58
- "@athenna/config": "1.1.9",
59
- "@athenna/ioc": "1.2.8",
60
- "@athenna/logger": "1.3.6",
61
- "@athenna/common": "1.0.0",
62
- "fastify": "3.27.4",
63
- "fastify-cors": "6.0.3",
64
- "fastify-rate-limit": "5.8.0"
57
+ "@athenna/artisan": "1.5.9",
58
+ "@athenna/common": "1.0.1",
59
+ "@athenna/config": "1.2.0",
60
+ "@athenna/ioc": "1.2.9",
61
+ "@athenna/logger": "1.3.7",
62
+ "@fastify/cors": "8.1.1",
63
+ "@fastify/helmet": "10.0.2",
64
+ "@fastify/rate-limit": "7.5.0",
65
+ "@fastify/swagger": "8.1.0",
66
+ "@fastify/swagger-ui": "1.1.0",
67
+ "fastify": "4.9.2"
65
68
  },
66
69
  "devDependencies": {
67
70
  "@japa/assert": "1.3.4",
@@ -35,6 +35,28 @@ export class Response {
35
35
  this.#response.send(data)
36
36
  }
37
37
 
38
+ /**
39
+ * Terminate the request sending the response body.
40
+ *
41
+ * @param {any} [data]
42
+ * @return {void}
43
+ */
44
+ json(data) {
45
+ this.#response.send(data)
46
+ }
47
+
48
+ /**
49
+ * Apply helmet in response.
50
+ *
51
+ * @param {import('@fastify/helmet').FastifyHelmetOptions} [options]
52
+ * @return {void}
53
+ */
54
+ async helmet(options) {
55
+ await this.#response.helmet(options)
56
+
57
+ return this
58
+ }
59
+
38
60
  /**
39
61
  * Set the response status code.
40
62
  *
@@ -54,7 +54,7 @@ export class FastifyHandler {
54
54
  * @return {any}
55
55
  */
56
56
  static createDoneHandler(handler) {
57
- return (req, res, done) => {
57
+ return async (req, res) => {
58
58
  const request = new Request(req)
59
59
  const response = new Response(res)
60
60
 
@@ -66,7 +66,6 @@ export class FastifyHandler {
66
66
  params: req.params,
67
67
  queries: req.query,
68
68
  data: req.data,
69
- next: done,
70
69
  })
71
70
  }
72
71
  }
@@ -78,7 +77,7 @@ export class FastifyHandler {
78
77
  * @return {any}
79
78
  */
80
79
  static createOnResponseHandler(handler) {
81
- return (req, res, done) => {
80
+ return async (req, res) => {
82
81
  const request = new Request(req)
83
82
  const response = new Response(res)
84
83
 
@@ -94,7 +93,6 @@ export class FastifyHandler {
94
93
  headers: res.getHeaders(),
95
94
  status: res.statusCode,
96
95
  responseTime: res.getResponseTime(),
97
- next: done,
98
96
  })
99
97
  }
100
98
  }
@@ -106,7 +104,7 @@ export class FastifyHandler {
106
104
  * @return {any}
107
105
  */
108
106
  static createErrorHandler(handler) {
109
- return (error, req, res) => {
107
+ return async (error, req, res) => {
110
108
  const request = new Request(req)
111
109
  const response = new Response(res)
112
110
 
@@ -52,7 +52,7 @@ export class HttpExceptionHandler {
52
52
  }
53
53
 
54
54
  const isInternalServerError = statusCode === 500
55
- const isDebugMode = Config.get('app.debug')
55
+ const isDebugMode = Config.get('app.debug', false)
56
56
 
57
57
  if (isInternalServerError && !isDebugMode) {
58
58
  body.name = 'Internal server error'
@@ -61,15 +61,15 @@ export class HttpExceptionHandler {
61
61
  delete body.stack
62
62
  }
63
63
 
64
+ response.status(statusCode).send(body)
65
+
64
66
  if (
65
67
  this.ignoreCodes.includes(code) ||
66
68
  this.ignoreStatuses.includes(statusCode)
67
69
  ) {
68
- return response.status(statusCode).send(body)
70
+ return
69
71
  }
70
72
 
71
- response.status(statusCode).send(body)
72
-
73
73
  if (error.prettify) {
74
74
  const prettyError = await error.prettify()
75
75
 
@@ -80,7 +80,7 @@ export class HttpExceptionHandler {
80
80
 
81
81
  const exception = new Exception(body.message, body.statusCode, body.code)
82
82
 
83
- exception.stack = body.stack
83
+ exception.stack = error.stack
84
84
 
85
85
  const prettyError = await exception.prettify()
86
86
 
@@ -97,7 +97,33 @@ export class HttpKernel {
97
97
  return
98
98
  }
99
99
 
100
- Server.registerCors(Config.get('http.cors'))
100
+ await Server.registerCors(Config.get('http.cors'))
101
+ }
102
+
103
+ /**
104
+ * Register helmet plugin.
105
+ *
106
+ * @return {Promise<void>}
107
+ */
108
+ async registerHelmet() {
109
+ if (Config.get('http.noHelmet')) {
110
+ return
111
+ }
112
+
113
+ await Server.registerHelmet(Config.get('http.helmet'))
114
+ }
115
+
116
+ /**
117
+ * Register swagger plugin.
118
+ *
119
+ * @return {Promise<void>}
120
+ */
121
+ async registerSwagger() {
122
+ if (Config.get('http.noSwagger')) {
123
+ return
124
+ }
125
+
126
+ await Server.registerSwagger(Config.get('http.swagger'))
101
127
  }
102
128
 
103
129
  /**
@@ -110,7 +136,7 @@ export class HttpKernel {
110
136
  return
111
137
  }
112
138
 
113
- Server.registerRateLimit(Config.get('http.rateLimit'))
139
+ await Server.registerRateLimit(Config.get('http.rateLimit'))
114
140
  }
115
141
 
116
142
  /**
@@ -142,8 +168,6 @@ export class HttpKernel {
142
168
 
143
169
  Server.use(async ctx => {
144
170
  await Log.channel('request').info(ctx)
145
-
146
- return ctx.next()
147
171
  }, 'terminate')
148
172
  }
149
173
 
@@ -159,8 +183,6 @@ export class HttpKernel {
159
183
 
160
184
  Server.use(async ctx => {
161
185
  ctx.data.requestId = Uuid.generate('ath')
162
-
163
- return ctx.next()
164
186
  }, 'handle')
165
187
  }
166
188
  }
@@ -7,7 +7,7 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
 
10
- import { Is } from '@athenna/common'
10
+ import { Is, Options, Route as RouteHelper } from '@athenna/common'
11
11
  import { removeSlashes } from '#src/Utils/removeSlashes'
12
12
  import { isMiddlewareContract } from '#src/Utils/isMiddlewareContract'
13
13
  import { UndefinedMethodException } from '#src/Exceptions/UndefinedMethodException'
@@ -62,6 +62,20 @@ export class Route {
62
62
  */
63
63
  #prefixes
64
64
 
65
+ /**
66
+ * Helmet options of this route.
67
+ *
68
+ * @type {any}
69
+ */
70
+ #helmetOptions
71
+
72
+ /**
73
+ * Swagger options of this route.
74
+ *
75
+ * @type {any}
76
+ */
77
+ #swaggerOptions
78
+
65
79
  /**
66
80
  * Creates a new instance of Route.
67
81
  *
@@ -79,6 +93,11 @@ export class Route {
79
93
  this.#handler = handler
80
94
  this.#routeMiddlewares = { handlers: [], terminators: [], interceptors: [] }
81
95
 
96
+ this.#helmetOptions = {}
97
+ this.#swaggerOptions = {}
98
+
99
+ RouteHelper.getParamsName(url).forEach(param => this.param(param))
100
+
82
101
  if (name) {
83
102
  this.name = name
84
103
  }
@@ -164,11 +183,174 @@ export class Route {
164
183
  return this
165
184
  }
166
185
 
186
+ /**
187
+ * Set up all helmet options for route.
188
+ *
189
+ * @param {any} options
190
+ * @param {boolean} [override]
191
+ * @return {Route}
192
+ */
193
+ helmet(options, override = true) {
194
+ if (!override) {
195
+ this.#helmetOptions = Options.create(this.#helmetOptions, options)
196
+
197
+ return this
198
+ }
199
+
200
+ this.#helmetOptions = options
201
+
202
+ return this
203
+ }
204
+
205
+ /**
206
+ * Set up all swagger options for route.
207
+ *
208
+ * @param {any} options
209
+ * @param {boolean} [override]
210
+ * @return {Route}
211
+ */
212
+ swagger(options, override = true) {
213
+ if (!override) {
214
+ this.#swaggerOptions = Options.create(this.#swaggerOptions, options)
215
+
216
+ return this
217
+ }
218
+
219
+ this.#swaggerOptions = options
220
+
221
+ return this
222
+ }
223
+
224
+ /**
225
+ * Set a summary for the route swagger docs.
226
+ *
227
+ * @param {string} summary
228
+ * @return {Route}
229
+ */
230
+ summary(summary) {
231
+ this.#swaggerOptions.summary = summary
232
+
233
+ return this
234
+ }
235
+
236
+ /**
237
+ * Set a description for the route swagger docs.
238
+ *
239
+ * @param {string} description
240
+ * @return {Route}
241
+ */
242
+ description(description) {
243
+ this.#swaggerOptions.description = description
244
+
245
+ return this
246
+ }
247
+
248
+ /**
249
+ * Set tags for the route swagger docs.
250
+ *
251
+ * @param {string} tags
252
+ * @return {Route}
253
+ */
254
+ tags(...tags) {
255
+ if (!this.#swaggerOptions.tags) {
256
+ this.#swaggerOptions.tags = []
257
+ }
258
+
259
+ tags.forEach(tag => this.#swaggerOptions.tags.push(tag))
260
+
261
+ return this
262
+ }
263
+
264
+ /**
265
+ * Set body param for the route swagger docs.
266
+ *
267
+ * @param {string} name
268
+ * @param {string} [type]
269
+ * @param {string} [description]
270
+ * @return {Route}
271
+ */
272
+ body(name, type = 'string', description = '') {
273
+ if (!this.#swaggerOptions.body) {
274
+ this.#swaggerOptions.body = {}
275
+ this.#swaggerOptions.body.type = 'object'
276
+ this.#swaggerOptions.body.properties = {}
277
+ }
278
+
279
+ this.#swaggerOptions.body.properties[name] = { type, description }
280
+
281
+ return this
282
+ }
283
+
284
+ /**
285
+ * Set param for the route swagger docs.
286
+ *
287
+ * @param {string} name
288
+ * @param {string} [type]
289
+ * @param {string} [description]
290
+ * @return {Route}
291
+ */
292
+ param(name, type = 'string', description = '') {
293
+ if (!this.#swaggerOptions.params) {
294
+ this.#swaggerOptions.params = {}
295
+ this.#swaggerOptions.params.type = 'object'
296
+ this.#swaggerOptions.params.properties = {}
297
+ }
298
+
299
+ this.#swaggerOptions.params.properties[name] = { type, description }
300
+
301
+ return this
302
+ }
303
+
304
+ /**
305
+ * Set query string for the route swagger docs.
306
+ *
307
+ * @param {string} name
308
+ * @param {string} [type]
309
+ * @param {string} [description]
310
+ * @return {Route}
311
+ */
312
+ queryString(name, type = 'string', description = '') {
313
+ if (!this.#swaggerOptions.queryString) {
314
+ this.#swaggerOptions.querystring = {}
315
+ this.#swaggerOptions.querystring.type = 'object'
316
+ this.#swaggerOptions.querystring.properties = {}
317
+ }
318
+
319
+ this.#swaggerOptions.querystring.properties[name] = { type, description }
320
+
321
+ return this
322
+ }
323
+
324
+ /**
325
+ * Set response for the route swagger docs.
326
+ *
327
+ * @param {number|any} statusCode
328
+ * @param {any} [response]
329
+ * @return {Route}
330
+ */
331
+ response(statusCode, response) {
332
+ if (!this.#swaggerOptions.response) {
333
+ this.#swaggerOptions.response = {}
334
+ }
335
+
336
+ if (!response) {
337
+ this.#swaggerOptions.response.default = response
338
+
339
+ return this
340
+ }
341
+
342
+ this.#swaggerOptions.response[statusCode] = response
343
+
344
+ return this
345
+ }
346
+
167
347
  toJSON() {
168
348
  const json = {
169
349
  url: this.#getUrl(),
170
350
  methods: this.#methods,
171
351
  middlewares: this.#routeMiddlewares,
352
+ helmetOptions: this.#helmetOptions,
353
+ swaggerOptions: this.#swaggerOptions,
172
354
  }
173
355
 
174
356
  if (Is.String(this.#handler)) {
@@ -13,14 +13,14 @@ export class RouteGroup {
13
13
  /**
14
14
  * All routes registered in the group.
15
15
  *
16
- * @type {(import('./Route.js').Route | RouteResource | RouteGroup)[]}
16
+ * @type {(Route | RouteResource | RouteGroup)[]}
17
17
  */
18
18
  routes
19
19
 
20
20
  /**
21
21
  * Creates a new instance of RouteGroup.
22
22
  *
23
- * @param {(import('./Route.js').Route | RouteResource | RouteGroup)[]} routes
23
+ * @param {(Route | RouteResource | RouteGroup)[]} routes
24
24
  */
25
25
  constructor(routes) {
26
26
  this.routes = routes
@@ -54,6 +54,34 @@ export class RouteGroup {
54
54
  return this
55
55
  }
56
56
 
57
+ /**
58
+ * Set up helmet options for route group.
59
+ *
60
+ * @param {any} options
61
+ * @return {RouteGroup}
62
+ */
63
+ helmet(options) {
64
+ this.routes.forEach(route => {
65
+ this.#invoke(route, 'helmet', [options, false])
66
+ })
67
+
68
+ return this
69
+ }
70
+
71
+ /**
72
+ * Set up swagger options for route group.
73
+ *
74
+ * @param {any} options
75
+ * @return {RouteGroup}
76
+ */
77
+ swagger(options) {
78
+ this.routes.forEach(route => {
79
+ this.#invoke(route, 'swagger', [options, false])
80
+ })
81
+
82
+ return this
83
+ }
84
+
57
85
  /**
58
86
  * Invoke a method from route.
59
87
  *
@@ -76,10 +76,10 @@ export class RouteResource {
76
76
  /**
77
77
  * Register only the methods in the array.
78
78
  *
79
- * @param {string[]} names
79
+ * @param {string} names
80
80
  * @return {RouteResource}
81
81
  */
82
- only(names) {
82
+ only(...names) {
83
83
  this.#filter(names, true).forEach(route => (route.deleted = true))
84
84
 
85
85
  return this
@@ -88,22 +88,76 @@ export class RouteResource {
88
88
  /**
89
89
  * Register all methods except the methods in the array.
90
90
  *
91
- * @param {string[]} names
91
+ * @param {string} names
92
92
  * @return {RouteResource}
93
93
  */
94
- except(names) {
94
+ except(...names) {
95
95
  this.#filter(names, false).forEach(route => (route.deleted = true))
96
96
 
97
97
  return this
98
98
  }
99
99
 
100
+ /**
101
+ * Set up helmet options for route resource.
102
+ *
103
+ * @param {string|any} action
104
+ * @param {any} [options]
105
+ * @return {RouteResource}
106
+ */
107
+ helmet(action, options) {
108
+ if (!options) {
109
+ this.routes.forEach(route => route.helmet(options))
110
+
111
+ return this
112
+ }
113
+
114
+ const resourceName = `${this.#resourceName}.${action}`
115
+
116
+ this.routes.forEach(route => {
117
+ if (route.name !== resourceName) {
118
+ return
119
+ }
120
+
121
+ route.helmet(options)
122
+ })
123
+
124
+ return this
125
+ }
126
+
127
+ /**
128
+ * Set up swagger options for route resource method.
129
+ *
130
+ * @param {string|any} action
131
+ * @param {any} [options]
132
+ * @return {RouteResource}
133
+ */
134
+ swagger(action, options) {
135
+ if (!options) {
136
+ this.routes.forEach(route => route.swagger(options))
137
+
138
+ return this
139
+ }
140
+
141
+ const resourceName = `${this.#resourceName}.${action}`
142
+
143
+ this.routes.forEach(route => {
144
+ if (route.name !== resourceName) {
145
+ return
146
+ }
147
+
148
+ route.swagger(options)
149
+ })
150
+
151
+ return this
152
+ }
153
+
100
154
  /**
101
155
  * Create the route.
102
156
  *
103
157
  * @param {string} url
104
158
  * @param {string[]} methods
105
159
  * @param {string} action
106
- * return {void}
160
+ * @return {void}
107
161
  */
108
162
  #makeRoute(url, methods, action) {
109
163
  let handler = ''
@@ -91,6 +91,17 @@ export class Router {
91
91
  return route
92
92
  }
93
93
 
94
+ /**
95
+ * Register a new vanila route using fastify options
96
+ * directly.
97
+ *
98
+ * @param {import('fastify').RouteOptions} options
99
+ * @return {void}
100
+ */
101
+ vanilaRoute(options) {
102
+ Server.getFastify().route(options)
103
+ }
104
+
94
105
  /**
95
106
  * Creates a new route group.
96
107
  *
@@ -256,6 +267,10 @@ export class Router {
256
267
  route.url,
257
268
  route.handler,
258
269
  route.middlewares,
270
+ {
271
+ helmet: route.helmetOptions,
272
+ schema: route.swaggerOptions,
273
+ },
259
274
  )
260
275
  })
261
276
  })
package/src/index.d.ts CHANGED
@@ -8,12 +8,34 @@
8
8
  */
9
9
 
10
10
  import { Facade } from '@athenna/ioc'
11
- import { FastifyReply, FastifyRequest } from 'fastify'
12
11
  import { Exception } from '@athenna/common'
12
+ import { OpenAPIV2, OpenAPIV3 } from 'openapi-types'
13
+ import { FastifyHelmetOptions } from '@fastify/helmet'
14
+ import { FastifyReply, FastifyRequest, RouteOptions } from 'fastify'
13
15
 
14
16
  export const Server: Facade & Http
15
17
  export const Route: Facade & Router.Router
16
18
 
19
+ interface FastifySwaggerSchema {
20
+ hide?: boolean
21
+ deprecated?: boolean
22
+ tags?: string[]
23
+ description?: string
24
+ summary?: string
25
+ body?: any
26
+ response?: any
27
+ consumes?: string[]
28
+ produces?: string[]
29
+ externalDocs?:
30
+ | OpenAPIV2.ExternalDocumentationObject
31
+ | OpenAPIV3.ExternalDocumentationObject
32
+ security?: Array<{ [securityLabel: string]: string[] }>
33
+ /**
34
+ * OpenAPI operation unique identifier
35
+ */
36
+ operationId?: string
37
+ }
38
+
17
39
  export class HttpKernel {
18
40
  /**
19
41
  * The application's global HTTP middlewares.
@@ -47,6 +69,20 @@ export class HttpKernel {
47
69
  */
48
70
  registerCors(): Promise<void>
49
71
 
72
+ /**
73
+ * Register helmet plugin.
74
+ *
75
+ * @return {Promise<void>}
76
+ */
77
+ registerHelmet(): Promise<void>
78
+
79
+ /**
80
+ * Register swagger plugin.
81
+ *
82
+ * @return {Promise<void>}
83
+ */
84
+ registerSwagger(): Promise<void>
85
+
50
86
  /**
51
87
  * Register rate limit plugin.
52
88
  *
@@ -127,19 +163,35 @@ export class Http {
127
163
  /**
128
164
  * Register the cors plugin to fastify server.
129
165
  *
130
- * @param {import('fastify-cors').FastifyCorsOptions} [options]
166
+ * @param {import('@fastify/cors').FastifyCorsOptions} [options]
131
167
  * @return {Http}
132
168
  */
133
- registerCors(options?: import('fastify-cors').FastifyCorsOptions): Http
169
+ registerCors(options?: import('@fastify/cors').FastifyCorsOptions): Http
170
+
171
+ /**
172
+ * Register the helmet plugin to fastify server.
173
+ *
174
+ * @param {import('@fastify/helmet').FastifyHelmetOptions} [options]
175
+ * @return {Http}
176
+ */
177
+ registerHelmet(options?: import('@fastify/helmet').FastifyHelmetOptions): Http
178
+
179
+ /**
180
+ * Register the swagger plugin to fastify server.
181
+ *
182
+ * @param {import('@fastify/swagger').SwaggerOptions} [options]
183
+ * @return {Http}
184
+ */
185
+ registerSwagger(options?: import('@fastify/swagger').SwaggerOptions): Http
134
186
 
135
187
  /**
136
188
  * Register the rate limit plugin to fastify server.
137
189
  *
138
- * @param {import('fastify-rate-limit').RateLimitPluginOptions} [options]
190
+ * @param {import('@fastify/rate-limit').RateLimitOptions} [options]
139
191
  * @return {Http}
140
192
  */
141
193
  registerRateLimit(
142
- options?: import('fastify-rate-limit').RateLimitPluginOptions,
194
+ options?: import('@fastify/rate-limit').RateLimitOptions,
143
195
  ): Http
144
196
 
145
197
  /**
@@ -322,6 +374,93 @@ declare module Router {
322
374
  prepend?: boolean,
323
375
  ): this
324
376
 
377
+ /**
378
+ * Set up all helmet options for route.
379
+ *
380
+ * @param {any} options
381
+ * @return {Route}
382
+ */
383
+ helmet(options: FastifyHelmetOptions): this
384
+
385
+ /**
386
+ * Set up all swagger options for route.
387
+ *
388
+ * @param {any} options
389
+ * @return {Route}
390
+ */
391
+ swagger(options: FastifySwaggerSchema): this
392
+
393
+ /**
394
+ * Set a summary for the route swagger docs.
395
+ *
396
+ * @param {string} summary
397
+ * @return {Route}
398
+ */
399
+ summary(summary: string): this
400
+
401
+ /**
402
+ * Set a description for the route swagger docs.
403
+ *
404
+ * @param {string} description
405
+ * @return {Route}
406
+ */
407
+ description(description: string): this
408
+
409
+ /**
410
+ * Set tags for the route swagger docs.
411
+ *
412
+ * @param {string} tags
413
+ * @return {Route}
414
+ */
415
+ tags(...tags: string[]): this
416
+
417
+ /**
418
+ * Set body param for the route swagger docs.
419
+ *
420
+ * @param {string} name
421
+ * @param {string} [type]
422
+ * @param {string} [description]
423
+ * @return {Route}
424
+ */
425
+ body(name: string, type?: string, description?: string): Route
426
+
427
+ /**
428
+ * Set param for the route swagger docs.
429
+ *
430
+ * @param {string} name
431
+ * @param {string} [type]
432
+ * @param {string} [description]
433
+ * @return {Route}
434
+ */
435
+ param(name, type?: string, description?: string): Route
436
+
437
+ /**
438
+ * Set query string for the route swagger docs.
439
+ *
440
+ * @param {string} name
441
+ * @param {string} [type]
442
+ * @param {string} [description]
443
+ * @return {Route}
444
+ */
445
+ queryString(name, type?: string, description?: string): Route
446
+
447
+ /**
448
+ * Set response for the route swagger docs.
449
+ *
450
+ * @param {any} response
451
+ * @return {Route}
452
+ */
453
+ response(response: any): this
454
+
455
+ /**
456
+ * Set response for the route swagger docs.
457
+ *
458
+ * @param {number} statusCode
459
+ * @param {any} response
460
+ * @return {Route}
461
+ */
462
+ response(statusCode: number, response: any): this
463
+
325
464
  toJSON(): any
326
465
  }
327
466
 
@@ -360,9 +499,57 @@ declare module Router {
360
499
  prepend?: boolean,
361
500
  ): this
362
501
 
502
+ /**
503
+ * Register only the methods in the array.
504
+ *
505
+ * @param {string} names
506
+ * @return {RouteResource}
507
+ */
363
508
  only(names: string[]): this
509
+ only(...names: string[]): this
364
510
 
511
+ /**
512
+ * Register all methods except the methods in the array.
513
+ *
514
+ * @param {string} names
515
+ * @return {RouteResource}
516
+ */
365
517
  except(names: string[]): this
518
+ except(...names: string[]): this
519
+
520
+ /**
521
+ * Set up helmet options for route resource.
522
+ *
523
+ * @param {FastifyHelmetOptions} options
524
+ * @return {RouteResource}
525
+ */
526
+ helmet(options: FastifyHelmetOptions): this
527
+
528
+ /**
529
+ * Set up helmet options for route resource.
530
+ *
531
+ * @param {string} action
532
+ * @param {FastifyHelmetOptions} options
533
+ * @return {RouteResource}
534
+ */
535
+ helmet(action: string, options: FastifyHelmetOptions): this
536
+
537
+ /**
538
+ * Set up swagger options for route resource method.
539
+ *
540
+ * @param {FastifySwaggerSchema} options
541
+ * @return {RouteResource}
542
+ */
543
+ swagger(options: FastifySwaggerSchema): this
544
+
545
+ /**
546
+ * Set up swagger options for route resource method.
547
+ *
548
+ * @param {string} action
549
+ * @param {FastifySwaggerSchema} options
550
+ * @return {RouteResource}
551
+ */
552
+ swagger(action: string, options: FastifySwaggerSchema): this
366
553
  }
367
554
 
368
555
  export class RouteGroup {
@@ -401,6 +588,22 @@ declare module Router {
401
588
  type?: 'terminate',
402
589
  prepend?: boolean,
403
590
  ): this
591
+
592
+ /**
593
+ * Set up helmet options for route group.
594
+ *
595
+ * @param {any} options
596
+ * @return {RouteGroup}
597
+ */
598
+ helmet(options: FastifyHelmetOptions): this
599
+
600
+ /**
601
+ * Set up swagger options for route group.
602
+ *
603
+ * @param {any} options
604
+ * @return {RouteGroup}
605
+ */
606
+ swagger(options: FastifySwaggerSchema): this
404
607
  }
405
608
 
406
609
  export class Router {
@@ -433,6 +636,15 @@ declare module Router {
433
636
  handler: string | HandlerContract,
434
637
  ): Route
435
638
 
639
+ /**
640
+ * Register a new vanila route using fastify options
641
+ * directly.
642
+ *
643
+ * @param {import('fastify').RouteOptions} options
644
+ * @return {void}
645
+ */
646
+ vanilaRoute(options: RouteOptions): void
647
+
436
648
  /**
437
649
  * Creates a new route group.
438
650
  *
@@ -549,44 +761,192 @@ declare module Router {
549
761
  }
550
762
 
551
763
  export interface RequestContract {
552
- ip: string
553
- method: string
554
- hostUrl: string
555
- baseUrl: string
556
- originalUrl: string
557
- body: any
558
- params: any
559
- queries: any
560
- headers: any
764
+ /**
765
+ * Get the request ip.
766
+ *
767
+ * @return {string}
768
+ */
769
+ get ip(): string
770
+ /**
771
+ * Get the request method.
772
+ *
773
+ * @return {string}
774
+ */
775
+ get method(): string
561
776
 
562
- param(param: string, defaultValue?: string): string | undefined
777
+ /**
778
+ * Get the host url from request.
779
+ *
780
+ * @return {string}
781
+ */
782
+ get hostUrl(): string
563
783
 
564
- query(query: string, defaultValue?: string): string | undefined
784
+ /**
785
+ * Get the base request url.
786
+ *
787
+ * @return {string}
788
+ */
789
+ get baseUrl(): string
790
+
791
+ /**
792
+ * Get the original request url.
793
+ *
794
+ * @return {string}
795
+ */
796
+ get originalUrl(): string
797
+
798
+ /**
799
+ * Get all body from request.
800
+ *
801
+ * @return {any}
802
+ */
803
+ get body(): any
804
+
805
+ /**
806
+ * Get all params from request.
807
+ *
808
+ * @return {any}
809
+ */
810
+ get params(): any
811
+
812
+ /**
813
+ * Get all queries from request.
814
+ *
815
+ * @return {any}
816
+ */
817
+ get queries(): any
818
+
819
+ /**
820
+ * Get all headers from request.
821
+ *
822
+ * @return {any}
823
+ */
824
+ get headers(): any
825
+
826
+ /**
827
+ * Get a value from the request params or the default value.
828
+ *
829
+ * @param {string} param
830
+ * @param {string} [defaultValue]
831
+ * @return {any}
832
+ */
833
+ param(param, defaultValue): any
565
834
 
566
- header(header: string, defaultValue?: string): string | string[] | undefined
835
+ /**
836
+ * Get a value from the request query param or the default value.
837
+ *
838
+ * @param {string} query
839
+ * @param {string} [defaultValue]
840
+ * @return {any}
841
+ */
842
+ query(query, defaultValue): any
567
843
 
568
- payload(payload: string, defaultValue?: string): any | undefined
844
+ /**
845
+ * Get a value from the request header or the default value.
846
+ *
847
+ * @param {string} header
848
+ * @param {string} [defaultValue]
849
+ * @return {any}
850
+ */
851
+ header(header, defaultValue): any
569
852
 
853
+ /**
854
+ * Get a value from the request body or the default value.
855
+ *
856
+ * @param {string} payload
857
+ * @param {string} [defaultValue]
858
+ * @return {any}
859
+ */
860
+ payload(payload, defaultValue): any
861
+ /**
862
+ * Get the default fastify request object.
863
+ *
864
+ * @return {import('fastify').FastifyRequest}
865
+ */
570
866
  getFastifyRequest(): FastifyRequest
571
867
  }
572
868
 
573
869
  export interface ResponseContract {
574
- send(data?: any): Promise<void> | void
870
+ /**
871
+ * Terminate the request sending the response body.
872
+ *
873
+ * @param {any} [data]
874
+ * @return {void}
875
+ */
876
+ send(data?: any): Promise<void>
575
877
 
576
- json(data?: any): Promise<void> | void
878
+ /**
879
+ * Terminate the request sending the response body.
880
+ *
881
+ * @param {any} [data]
882
+ * @return {void}
883
+ */
884
+ json(data?: any): Promise<void>
577
885
 
886
+ /**
887
+ * Apply helmet in response.
888
+ *
889
+ * @param {import('@fastify/helmet').FastifyHelmetOptions} [options]
890
+ * @return {void}
891
+ */
892
+ helmet(options?: FastifyHelmetOptions): Promise<void>
893
+
894
+ /**
895
+ * Set the response status code.
896
+ *
897
+ * @param {number} code
898
+ * @return {Response}
899
+ */
578
900
  status(code: number): this
579
901
 
902
+ /**
903
+ * Remove some header from the response.
904
+ *
905
+ * @param {string} header
906
+ * @return {Response}
907
+ */
580
908
  removeHeader(header: string): this
581
909
 
910
+ /**
911
+ * Add some header to the response.
912
+ *
913
+ * @param {string} header
914
+ * @param {any} value
915
+ * @return {Response}
916
+ */
582
917
  header(header: string, value: any): this
583
918
 
919
+ /**
920
+ * Only add some header to the response if it's not defined yet.
921
+ *
922
+ * @param {string} header
923
+ * @param {any} value
924
+ * @return {Response}
925
+ */
584
926
  safeHeader(header: string, value: any): this
585
927
 
928
+ /**
929
+ * Redirect the response to other url with different status code.
930
+ *
931
+ * @param {string} url
932
+ * @return {void}
933
+ */
586
934
  redirectTo(url: string): Promise<void> | void
587
935
 
588
- redirectTo(url: string, statusCode: number): Promise<void> | void
936
+ /**
937
+ * Redirect the response to other url with different status code.
938
+ *
939
+ * @param {string} url
940
+ * @param {number} statusCode
941
+ * @return {void}
942
+ */
943
+ redirectTo(url: string, statusCode: number): Promise<void>
589
944
 
945
+ /**
946
+ * Get the default fastify response object.
947
+ *
948
+ * @return {import('fastify').FastifyReply}
949
+ */
590
950
  getFastifyResponse(): FastifyReply
591
951
  }
592
952
 
@@ -606,10 +966,6 @@ export class HttpLoader {
606
966
  static loadTemplates(): any[]
607
967
  }
608
968
 
609
- export interface NextContract {
610
- (...params: any[]): void
611
- }
612
-
613
969
  export interface ContextContract {
614
970
  request: RequestContract
615
971
  response: ResponseContract
@@ -633,7 +989,6 @@ export interface HandleContextContract {
633
989
  data: any
634
990
  params: any
635
991
  queries: any
636
- next: NextContract
637
992
  }
638
993
 
639
994
  export interface InterceptContextContract {
@@ -656,7 +1011,6 @@ export interface TerminateContextContract {
656
1011
  headers: any
657
1012
  status: number
658
1013
  responseTime: number
659
- next: NextContract
660
1014
  }
661
1015
 
662
1016
  export interface ErrorHandlerContract {
package/src/index.js CHANGED
@@ -10,8 +10,7 @@
10
10
  import { Options } from '@athenna/common'
11
11
 
12
12
  import fastify from 'fastify'
13
- import fastifyCors from 'fastify-cors'
14
- import fastifyRateLimit from 'fastify-rate-limit'
13
+
15
14
  import { FastifyHandler } from '#src/Handlers/FastifyHandler'
16
15
 
17
16
  export * from './Facades/Route.js'
@@ -82,12 +81,12 @@ export class Http {
82
81
  /**
83
82
  * Register a new fastify plugin.
84
83
  *
85
- * @param {import('fastify').FastifyPluginCallback<import('fastify').FastifyPluginOptions>} plugin
86
- * @param {import('fastify').FastifyRegisterOptions<import('fastify').FastifyPluginOptions>} [options]
84
+ * @param {any} plugin
85
+ * @param {any} [options]
87
86
  * @return {Http}
88
87
  */
89
- register(plugin, options) {
90
- this.#server.register(plugin, options)
88
+ async register(plugin, options = {}) {
89
+ await this.#server.register(plugin, options)
91
90
 
92
91
  return this
93
92
  }
@@ -95,25 +94,46 @@ export class Http {
95
94
  /**
96
95
  * Register the cors plugin to fastify server.
97
96
  *
98
- * @param {import('fastify-cors').FastifyCorsOptions} [options]
97
+ * @param {import('@fastify/cors').FastifyCorsOptions} [options]
99
98
  * @return {Http}
100
99
  */
101
- registerCors(options) {
102
- this.register(fastifyCors, options)
100
+ async registerCors(options) {
101
+ return this.register(import('@fastify/cors'), options)
102
+ }
103
103
 
104
- return this
104
+ /**
105
+ * Register the helmet plugin to fastify server.
106
+ *
107
+ * @param {import('@fastify/helmet').FastifyHelmetOptions} [options]
108
+ * @return {Http}
109
+ */
110
+ async registerHelmet(options) {
111
+ return this.register(import('@fastify/helmet'), options)
105
112
  }
106
113
 
107
114
  /**
108
- * Register the rate limit plugin to fastify server.
115
+ * Register the swagger plugin to fastify server.
109
116
  *
110
- * @param {import('fastify-rate-limit').RateLimitPluginOptions} [options]
117
+ * @param {{
118
+ * ui: import('@fastify/swagger-ui').FastifySwaggerUiOptions,
119
+ * configurations: import('@fastify/swagger').SwaggerOptions
120
+ * }} [options]
111
121
  * @return {Http}
112
122
  */
113
- registerRateLimit(options) {
114
- this.register(fastifyRateLimit, options)
123
+ async registerSwagger(options = {}) {
124
+ await this.register(import('@fastify/swagger'), options.configurations)
115
125
 
116
- return this
126
+ return this.register(import('@fastify/swagger-ui'), options.ui)
127
+ }
128
+
129
+ /**
130
+ * Register the rate limit plugin to fastify server.
131
+ *
132
+ * @param {import('@fastify/rate-limit').RateLimitOptions} [options]
133
+ * @return {Http}
134
+ */
135
+ async registerRateLimit(options) {
136
+ return this.register(import('@fastify/rate-limit'), options)
117
137
  }
118
138
 
119
139
  /**
@@ -186,7 +206,7 @@ export class Http {
186
206
  async listen(port = 1335, host = '0.0.0.0') {
187
207
  this.#isListening = true
188
208
 
189
- return this.#server.listen(port, host)
209
+ return this.#server.listen({ port, host })
190
210
  }
191
211
 
192
212
  /**
@@ -209,9 +229,10 @@ export class Http {
209
229
  * @param {string[]} methods
210
230
  * @param {any} handler
211
231
  * @param {any} [middlewares]
232
+ * @param {import('fastify').RouteOptions} [otherOptions]
212
233
  * @return {void}
213
234
  */
214
- route(url, methods, handler, middlewares) {
235
+ route(url, methods, handler, middlewares, otherOptions = {}) {
215
236
  const { handlers, terminators, interceptors } = Options.create(
216
237
  middlewares,
217
238
  {
@@ -230,6 +251,7 @@ export class Http {
230
251
  handler: FastifyHandler.createRequestHandler(handler),
231
252
  preHandler: handlers.map(m => FastifyHandler.createDoneHandler(m)),
232
253
  onSend: interceptors.map(m => FastifyHandler.createOnSendHandler(m)),
254
+ ...otherOptions,
233
255
  })
234
256
  }
235
257
 
@@ -239,10 +261,11 @@ export class Http {
239
261
  * @param {string} url
240
262
  * @param {any} handler
241
263
  * @param {any} [middlewares]
264
+ * @param {import('fastify').RouteOptions} [otherOptions]
242
265
  * @return {void}
243
266
  */
244
- get(url, handler, middlewares) {
245
- this.route(url, ['GET'], handler, middlewares)
267
+ get(url, handler, middlewares, otherOptions) {
268
+ this.route(url, ['GET'], handler, middlewares, otherOptions)
246
269
  }
247
270
 
248
271
  /**
@@ -251,10 +274,11 @@ export class Http {
251
274
  * @param {string} url
252
275
  * @param {any} handler
253
276
  * @param {any} [middlewares]
277
+ * @param {import('fastify').RouteOptions} [otherOptions]
254
278
  * @return {void}
255
279
  */
256
- head(url, handler, middlewares) {
257
- this.route(url, ['HEAD'], handler, middlewares)
280
+ head(url, handler, middlewares, otherOptions) {
281
+ this.route(url, ['HEAD'], handler, middlewares, otherOptions)
258
282
  }
259
283
 
260
284
  /**
@@ -263,10 +287,11 @@ export class Http {
263
287
  * @param {string} url
264
288
  * @param {any} handler
265
289
  * @param {any} [middlewares]
290
+ * @param {import('fastify').RouteOptions} [otherOptions]
266
291
  * @return {void}
267
292
  */
268
- post(url, handler, middlewares) {
269
- this.route(url, ['POST'], handler, middlewares)
293
+ post(url, handler, middlewares, otherOptions) {
294
+ this.route(url, ['POST'], handler, middlewares, otherOptions)
270
295
  }
271
296
 
272
297
  /**
@@ -275,10 +300,11 @@ export class Http {
275
300
  * @param {string} url
276
301
  * @param {any} handler
277
302
  * @param {any} [middlewares]
303
+ * @param {import('fastify').RouteOptions} [otherOptions]
278
304
  * @return {void}
279
305
  */
280
- put(url, handler, middlewares) {
281
- this.route(url, ['PUT'], handler, middlewares)
306
+ put(url, handler, middlewares, otherOptions) {
307
+ this.route(url, ['PUT'], handler, middlewares, otherOptions)
282
308
  }
283
309
 
284
310
  /**
@@ -287,10 +313,11 @@ export class Http {
287
313
  * @param {string} url
288
314
  * @param {any} handler
289
315
  * @param {any} [middlewares]
316
+ * @param {import('fastify').RouteOptions} [otherOptions]
290
317
  * @return {void}
291
318
  */
292
- patch(url, handler, middlewares) {
293
- this.route(url, ['PATCH'], handler, middlewares)
319
+ patch(url, handler, middlewares, otherOptions) {
320
+ this.route(url, ['PATCH'], handler, middlewares, otherOptions)
294
321
  }
295
322
 
296
323
  /**
@@ -299,10 +326,11 @@ export class Http {
299
326
  * @param {string} url
300
327
  * @param {any} handler
301
328
  * @param {any} [middlewares]
329
+ * @param {import('fastify').RouteOptions} [otherOptions]
302
330
  * @return {void}
303
331
  */
304
- delete(url, handler, middlewares) {
305
- this.route(url, ['DELETE'], handler, middlewares)
332
+ delete(url, handler, middlewares, otherOptions) {
333
+ this.route(url, ['DELETE'], handler, middlewares, otherOptions)
306
334
  }
307
335
 
308
336
  /**
@@ -311,9 +339,10 @@ export class Http {
311
339
  * @param {string} url
312
340
  * @param {any} handler
313
341
  * @param {any} [middlewares]
342
+ * @param {import('fastify').RouteOptions} [otherOptions]
314
343
  * @return {void}
315
344
  */
316
- options(url, handler, middlewares) {
317
- this.route(url, ['OPTIONS'], handler, middlewares)
345
+ options(url, handler, middlewares, otherOptions) {
346
+ this.route(url, ['OPTIONS'], handler, middlewares, otherOptions)
318
347
  }
319
348
  }
@@ -10,16 +10,16 @@ export class {{ namePascal }} {
10
10
  * in your controller.
11
11
  *
12
12
  * @param {import('@athenna/http').HandleContextContract} ctx
13
+ * @return {Promise<void>}
13
14
  */
14
- async handle({ next }) {
15
- next()
16
- }
15
+ async handle(ctx) {}
17
16
 
18
17
  /**
19
18
  * Intercept method is executed before the response
20
19
  * has been sent.
21
20
  *
22
21
  * @param {import('@athenna/http').InterceptContextContract} ctx
22
+ * @return {Promise<any>}
23
23
  */
24
24
  async intercept({ body }) {
25
25
  body.intercepted = true
@@ -32,9 +32,7 @@ export class {{ namePascal }} {
32
32
  * has been sent.
33
33
  *
34
34
  * @param {import('@athenna/http').TerminateContextContract} ctx
35
- * @return any
35
+ * @return {Promise<void>}
36
36
  */
37
- async terminate({ next }) {
38
- next()
39
- }
37
+ async terminate(ctx) {}
40
38
  }