@athenna/http 4.22.0 → 4.24.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.
@@ -7,65 +7,210 @@
7
7
  * file that was distributed with this source code.
8
8
  */
9
9
  import type { FastifyReply } from 'fastify';
10
+ import type { SendOptions } from '@fastify/static';
11
+ import type { Request } from '#src/context/Request';
10
12
  import type { FastifyHelmetOptions } from '@fastify/helmet';
11
13
  export declare class Response {
12
14
  /**
13
15
  * The fastify response object.
14
16
  */
15
17
  response: FastifyReply;
16
- constructor(response: FastifyReply);
17
18
  /**
18
- * Verify if the response has been already sent.
19
+ * The request object from request context.
20
+ */
21
+ request: Request;
22
+ constructor(response: FastifyReply, request?: Request);
23
+ /**
24
+ * Verify if the response has been already sent. Keep
25
+ * in mind that this method will only return `true`
26
+ * after `response.send()`, `response.view()`,
27
+ * `response.sendFile()` or `response.download()` methods
28
+ * call.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * if (response.sent) {
33
+ * // do something
34
+ * }
35
+ * ```
19
36
  */
20
37
  get sent(): boolean;
21
38
  /**
22
- * Get the response body sent in response.
39
+ * Get the response body sent in response. Keep
40
+ * in mind that this method will only return `true`
41
+ * after `response.send()`, `response.view()`,
42
+ * `response.sendFile()` or `response.download()` methods
43
+ * call.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const { createdAt, updatedAt } = response.body
48
+ * ```
23
49
  */
24
50
  get body(): any | any[];
25
51
  /**
26
- * Get the status code sent in response.
52
+ * Get the status code sent in response. Keep
53
+ * in mind that this method will only return `true`
54
+ * after `response.send()`, `response.view()`,
55
+ * `response.sendFile()` or `response.download()` methods
56
+ * call.
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * if (response.statusCode !== 200) {
61
+ * // do something
62
+ * }
63
+ * ```
27
64
  */
28
65
  get statusCode(): number;
29
66
  /**
30
- * Get the headers sent in response.
67
+ * Get the headers set in the response.
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * const headers = response.headers
72
+ * ```
31
73
  */
32
74
  get headers(): any;
33
75
  /**
34
- * Get the time in MS of how much the request has taken to response.
76
+ * Get the time in MS of how much the request has
77
+ * taken to response. Keep in mind that this method
78
+ * will only return `true` after `response.send()`,
79
+ * `response.view()`, `response.sendFile()` or
80
+ * `response.download()` methods call.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * console.log(response.responseTime) // 1000
85
+ * ```
35
86
  */
36
87
  get responseTime(): number;
88
+ /**
89
+ * Terminate the request sending a view to be rendered.
90
+ *
91
+ * @example
92
+ * ```ts
93
+ * return response.view('welcome', { name: 'lenon' })
94
+ * ```
95
+ */
96
+ view(view: string, data?: any): Promise<Response>;
37
97
  /**
38
98
  * Terminate the request sending the response body or not.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * return response.send({ name: 'lenon' })
103
+ * ```
39
104
  */
40
105
  send(data?: any): Promise<Response>;
106
+ /**
107
+ * @example
108
+ * ```ts
109
+ * return response.sendFile('img.png')
110
+ * ```
111
+ */
112
+ sendFile(filename: string, filepath?: string): Promise<Response>;
113
+ /**
114
+ * @example
115
+ * ```ts
116
+ * return response.sendFile('img.png', { cacheControl: false })
117
+ * ```
118
+ */
119
+ sendFile(filename: string, options?: string | SendOptions): Promise<Response>;
120
+ /**
121
+ * @example
122
+ * ```ts
123
+ * return response.sendFile('img.png', Path.tmp(), {
124
+ * cacheControl: false
125
+ * })
126
+ * ```
127
+ */
128
+ sendFile(filename: string, filepath?: string, options?: SendOptions): Promise<Response>;
129
+ /**
130
+ * @example
131
+ * ```ts
132
+ * return response.download('img.png', 'custom-img.png')
133
+ * ```
134
+ */
135
+ download(filepath: string, filename: string): Promise<Response>;
136
+ /**
137
+ * @example
138
+ * ```ts
139
+ * return response.download('img.png', 'custom-img.png', {
140
+ * cacheControl: false
141
+ * })
142
+ * ```
143
+ */
144
+ download(filepath: string, filename: string, options?: SendOptions): Promise<Response>;
41
145
  /**
42
146
  * Set the response status code.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * return response.status(200).send()
151
+ * ```
43
152
  */
44
153
  status(code: number): Response;
45
154
  /**
46
155
  * Add some header to the response.
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * response.header('content-type', 'application/json')
160
+ *
161
+ * return response.header('accept-encoding', 'gzip').send(user)
162
+ * ```
47
163
  */
48
164
  header(header: string, value: any): Response;
49
165
  /**
50
166
  * Verify if response has some header.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * if (response.hasHeader('content-type')) {
171
+ * // do something
172
+ * }
173
+ * ```
51
174
  */
52
175
  hasHeader(header: string): boolean;
53
176
  /**
54
- * Add some header safelly to the response. This means that the header is not
55
- * going to be added if is already set.
177
+ * Add some header safely to the response. This means that
178
+ * the header is not going to be added if is already set.
179
+ *
180
+ * @example
181
+ * ```ts
182
+ * response.safeHeader('content-type', 'application/json')
183
+ * ```
56
184
  */
57
185
  safeHeader(header: string, value: any): Response;
58
186
  /**
59
187
  * Remove some header from the response.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * response.removeHeader('content-type')
192
+ * ```
60
193
  */
61
194
  removeHeader(header: string): Response;
62
195
  /**
63
- * Redirect the response to other url. You can also set a different status code
64
- * for the redirect.
196
+ * Redirect the response to other url. You can also set a
197
+ * different status code for the redirect.
198
+ *
199
+ * @example
200
+ * ```ts
201
+ * return response.redirect('users', 304)
202
+ * ```
65
203
  */
66
204
  redirectTo(url: string, status?: number): Promise<Response>;
67
205
  /**
68
206
  * Apply helmet in response.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * return response
211
+ * .helmet({ enableCSPNonces: false })
212
+ * .view('profile', user)
213
+ * ```
69
214
  */
70
215
  helmet(options: FastifyHelmetOptions): Response;
71
216
  }
@@ -6,50 +6,145 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
+ import { View } from '@athenna/view';
9
10
  export class Response {
10
- constructor(response) {
11
+ constructor(response, request) {
11
12
  this.response = response;
13
+ this.request = request;
12
14
  }
13
15
  /**
14
- * Verify if the response has been already sent.
16
+ * Verify if the response has been already sent. Keep
17
+ * in mind that this method will only return `true`
18
+ * after `response.send()`, `response.view()`,
19
+ * `response.sendFile()` or `response.download()` methods
20
+ * call.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * if (response.sent) {
25
+ * // do something
26
+ * }
27
+ * ```
15
28
  */
16
29
  get sent() {
17
30
  return this.response.sent;
18
31
  }
19
32
  /**
20
- * Get the response body sent in response.
33
+ * Get the response body sent in response. Keep
34
+ * in mind that this method will only return `true`
35
+ * after `response.send()`, `response.view()`,
36
+ * `response.sendFile()` or `response.download()` methods
37
+ * call.
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * const { createdAt, updatedAt } = response.body
42
+ * ```
21
43
  */
22
44
  get body() {
23
45
  return this.response.body;
24
46
  }
25
47
  /**
26
- * Get the status code sent in response.
48
+ * Get the status code sent in response. Keep
49
+ * in mind that this method will only return `true`
50
+ * after `response.send()`, `response.view()`,
51
+ * `response.sendFile()` or `response.download()` methods
52
+ * call.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * if (response.statusCode !== 200) {
57
+ * // do something
58
+ * }
59
+ * ```
27
60
  */
28
61
  get statusCode() {
29
62
  return this.response.statusCode;
30
63
  }
31
64
  /**
32
- * Get the headers sent in response.
65
+ * Get the headers set in the response.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const headers = response.headers
70
+ * ```
33
71
  */
34
72
  get headers() {
35
73
  return this.response.getHeaders();
36
74
  }
37
75
  /**
38
- * Get the time in MS of how much the request has taken to response.
76
+ * Get the time in MS of how much the request has
77
+ * taken to response. Keep in mind that this method
78
+ * will only return `true` after `response.send()`,
79
+ * `response.view()`, `response.sendFile()` or
80
+ * `response.download()` methods call.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * console.log(response.responseTime) // 1000
85
+ * ```
39
86
  */
40
87
  get responseTime() {
41
- return this.response.getResponseTime();
88
+ return this.response.elapsedTime;
89
+ }
90
+ /**
91
+ * Terminate the request sending a view to be rendered.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * return response.view('welcome', { name: 'lenon' })
96
+ * ```
97
+ */
98
+ async view(view, data) {
99
+ const content = await View.render(view, { ...data, request: this.request });
100
+ await this.safeHeader('Content-Type', 'text/html; charset=utf-8').response.send(content);
101
+ this.response.body = content;
102
+ return this;
42
103
  }
43
104
  /**
44
105
  * Terminate the request sending the response body or not.
106
+ *
107
+ * @example
108
+ * ```ts
109
+ * return response.send({ name: 'lenon' })
110
+ * ```
45
111
  */
46
112
  async send(data) {
47
113
  await this.response.send(data);
48
114
  this.response.body = data;
49
115
  return this;
50
116
  }
117
+ /**
118
+ * Terminate the request sending a file.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * return response.sendFile('img.png')
123
+ * ```
124
+ */
125
+ async sendFile(filename, filepath, options) {
126
+ await this.response.sendFile(filename, filepath, options);
127
+ return this;
128
+ }
129
+ /**
130
+ * Terminate the request sending a file with custom name.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * return response.download('img.png', 'custom-img.png')
135
+ * ```
136
+ */
137
+ async download(filepath, filename, options) {
138
+ await this.response.download(filename, filepath, options);
139
+ return this;
140
+ }
51
141
  /**
52
142
  * Set the response status code.
143
+ *
144
+ * @example
145
+ * ```ts
146
+ * return response.status(200).send()
147
+ * ```
53
148
  */
54
149
  status(code) {
55
150
  this.response.status(code);
@@ -57,6 +152,13 @@ export class Response {
57
152
  }
58
153
  /**
59
154
  * Add some header to the response.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * response.header('content-type', 'application/json')
159
+ *
160
+ * return response.header('accept-encoding', 'gzip').send(user)
161
+ * ```
60
162
  */
61
163
  header(header, value) {
62
164
  this.response.header(header, value);
@@ -64,13 +166,25 @@ export class Response {
64
166
  }
65
167
  /**
66
168
  * Verify if response has some header.
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * if (response.hasHeader('content-type')) {
173
+ * // do something
174
+ * }
175
+ * ```
67
176
  */
68
177
  hasHeader(header) {
69
178
  return this.response.hasHeader(header);
70
179
  }
71
180
  /**
72
- * Add some header safelly to the response. This means that the header is not
73
- * going to be added if is already set.
181
+ * Add some header safely to the response. This means that
182
+ * the header is not going to be added if is already set.
183
+ *
184
+ * @example
185
+ * ```ts
186
+ * response.safeHeader('content-type', 'application/json')
187
+ * ```
74
188
  */
75
189
  safeHeader(header, value) {
76
190
  this.response.header(header, value);
@@ -78,14 +192,24 @@ export class Response {
78
192
  }
79
193
  /**
80
194
  * Remove some header from the response.
195
+ *
196
+ * @example
197
+ * ```ts
198
+ * response.removeHeader('content-type')
199
+ * ```
81
200
  */
82
201
  removeHeader(header) {
83
202
  this.response.removeHeader(header);
84
203
  return this;
85
204
  }
86
205
  /**
87
- * Redirect the response to other url. You can also set a different status code
88
- * for the redirect.
206
+ * Redirect the response to other url. You can also set a
207
+ * different status code for the redirect.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * return response.redirect('users', 304)
212
+ * ```
89
213
  */
90
214
  async redirectTo(url, status) {
91
215
  if (status) {
@@ -97,6 +221,13 @@ export class Response {
97
221
  }
98
222
  /**
99
223
  * Apply helmet in response.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * return response
228
+ * .helmet({ enableCSPNonces: false })
229
+ * .view('profile', user)
230
+ * ```
100
231
  */
101
232
  helmet(options) {
102
233
  this.response.helmet(options);
@@ -6,9 +6,9 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import type { InterceptHandler, TerminateHandler } from '#src/types';
10
9
  import type { RequestHandler } from '#src/types/contexts/Context';
11
10
  import type { ErrorHandler } from '#src/types/contexts/ErrorContext';
11
+ import type { InterceptHandler, TerminateHandler } from '#src/types';
12
12
  import type { FastifyReply, FastifyRequest, RouteHandlerMethod } from 'fastify';
13
13
  export declare class FastifyHandler {
14
14
  /**
@@ -16,20 +16,12 @@ export class FastifyHandler {
16
16
  */
17
17
  static request(handler) {
18
18
  return async (req, res) => {
19
- const request = new Request(req);
20
- const response = new Response(res);
21
19
  if (!req.data) {
22
20
  req.data = {};
23
21
  }
24
- await handler({
25
- request,
26
- response,
27
- data: req.data,
28
- body: req.body,
29
- params: req.params,
30
- queries: req.query,
31
- headers: req.headers
32
- });
22
+ const request = new Request(req);
23
+ const response = new Response(res, request);
24
+ return handler({ request, response, data: req.data });
33
25
  };
34
26
  }
35
27
  /**
@@ -43,8 +35,11 @@ export class FastifyHandler {
43
35
  */
44
36
  static intercept(handler) {
45
37
  return async (req, res, payload) => {
38
+ if (!req.data) {
39
+ req.data = {};
40
+ }
46
41
  const request = new Request(req);
47
- const response = new Response(res);
42
+ const response = new Response(res, request);
48
43
  if (Is.Json(payload)) {
49
44
  payload = JSON.parse(payload);
50
45
  }
@@ -53,11 +48,7 @@ export class FastifyHandler {
53
48
  request,
54
49
  response,
55
50
  status: response.statusCode,
56
- data: req.data,
57
- body: res.body,
58
- params: req.params,
59
- queries: req.query,
60
- headers: req.headers
51
+ data: req.data
61
52
  });
62
53
  res.body = payload;
63
54
  if (Is.Object(payload)) {
@@ -71,18 +62,17 @@ export class FastifyHandler {
71
62
  */
72
63
  static terminate(handler) {
73
64
  return async (req, res) => {
65
+ if (!req.data) {
66
+ req.data = {};
67
+ }
74
68
  const request = new Request(req);
75
- const response = new Response(res);
69
+ const response = new Response(res, request);
76
70
  await handler({
77
71
  request,
78
72
  response,
79
- params: req.params,
80
- queries: req.query,
81
73
  data: req.data,
82
- body: res.body || req.body,
83
- headers: res.getHeaders(),
84
74
  status: res.statusCode,
85
- responseTime: res.getResponseTime()
75
+ responseTime: res.elapsedTime
86
76
  });
87
77
  };
88
78
  }
@@ -91,16 +81,15 @@ export class FastifyHandler {
91
81
  */
92
82
  static error(handler) {
93
83
  return async (error, req, res) => {
84
+ if (!req.data) {
85
+ req.data = {};
86
+ }
94
87
  const request = new Request(req);
95
- const response = new Response(res);
88
+ const response = new Response(res, request);
96
89
  await handler({
97
90
  request,
98
91
  response,
99
92
  data: req.data,
100
- body: res.body || req.body,
101
- params: req.params,
102
- queries: req.query,
103
- headers: req.headers,
104
93
  error
105
94
  });
106
95
  };
@@ -24,6 +24,10 @@ export declare class HttpKernel {
24
24
  * Register the @fastify/rate-limit plugin in the Http server.
25
25
  */
26
26
  registerRateLimit(): Promise<void>;
27
+ /**
28
+ * Register the @fastify/static plugin in the Http server.
29
+ */
30
+ registerStatic(): Promise<void>;
27
31
  /**
28
32
  * Register the cls-rtracer plugin in the Http server.
29
33
  */
@@ -13,13 +13,14 @@ import { Log } from '@athenna/logger';
13
13
  import { Config } from '@athenna/config';
14
14
  import { sep, isAbsolute, resolve } from 'node:path';
15
15
  import { Annotation } from '@athenna/ioc';
16
- import { File, Exec, Module, String } from '@athenna/common';
16
+ import { File, Path, Exec, Module, String } from '@athenna/common';
17
17
  import { HttpExceptionHandler } from '#src/handlers/HttpExceptionHandler';
18
18
  const corsPlugin = await Module.safeImport('@fastify/cors');
19
19
  const helmetPlugin = await Module.safeImport('@fastify/helmet');
20
20
  const swaggerPlugin = await Module.safeImport('@fastify/swagger');
21
21
  const swaggerUiPlugin = await Module.safeImport('@fastify/swagger-ui');
22
22
  const rateLimitPlugin = await Module.safeImport('@fastify/rate-limit');
23
+ const staticPlugin = await Module.safeImport('@fastify/static');
23
24
  const rTracerPlugin = await Module.safeImport('cls-rtracer');
24
25
  export class HttpKernel {
25
26
  /**
@@ -90,6 +91,20 @@ export class HttpKernel {
90
91
  }
91
92
  await Server.plugin(rateLimitPlugin, this.getConfig('http.rateLimit'));
92
93
  }
94
+ /**
95
+ * Register the @fastify/static plugin in the Http server.
96
+ */
97
+ async registerStatic() {
98
+ if (Config.is('http.static.enabled', false)) {
99
+ debug('Not able to register static plugin. Set the http.static.enabled configuration as true.');
100
+ return;
101
+ }
102
+ if (!staticPlugin) {
103
+ debug('Not able to register static plugin. Install @fastify/static package.');
104
+ return;
105
+ }
106
+ await Server.plugin(staticPlugin, this.getConfig('http.static'));
107
+ }
93
108
  /**
94
109
  * Register the cls-rtracer plugin in the Http server.
95
110
  */
@@ -151,17 +151,22 @@ export class ServerImpl {
151
151
  });
152
152
  return;
153
153
  }
154
- this.fastify.route({
154
+ const { middlewares, interceptors, terminators } = options.middlewares;
155
+ const route = {
155
156
  url: options.url,
156
157
  method: options.methods,
157
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
158
- // @ts-ignore
159
- handler: FastifyHandler.request(options.handler),
160
- preHandler: options.middlewares.middlewares.map(m => FastifyHandler.handle(m)),
161
- onSend: options.middlewares.interceptors.map(m => FastifyHandler.intercept(m)),
162
- onResponse: options.middlewares.terminators.map(m => FastifyHandler.terminate(m)),
163
- ...options.fastify
164
- });
158
+ handler: FastifyHandler.request(options.handler)
159
+ };
160
+ if (middlewares.length) {
161
+ route.preHandler = middlewares.map(m => FastifyHandler.handle(m));
162
+ }
163
+ if (interceptors.length) {
164
+ route.onSend = interceptors.map(i => FastifyHandler.intercept(i));
165
+ }
166
+ if (terminators.length) {
167
+ route.onResponse = terminators.map(t => FastifyHandler.terminate(t));
168
+ }
169
+ this.fastify.route({ ...route, ...options.fastify });
165
170
  }
166
171
  /**
167
172
  * Add a new GET route to the http server.
@@ -12,9 +12,5 @@ export type Context = {
12
12
  request: Request;
13
13
  response: Response;
14
14
  data: any;
15
- body: any;
16
- params: any;
17
- queries: any;
18
- headers: any;
19
15
  };
20
16
  export type RequestHandler = (ctx: Context) => any | Promise<any>;
@@ -6,16 +6,8 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import { Request } from '#src/context/Request';
10
- import { Response } from '#src/context/Response';
11
- export type ErrorContext = {
12
- request: Request;
13
- response: Response;
14
- data: any;
15
- body: any;
9
+ import type { Context } from '#src/types/contexts/Context';
10
+ export type ErrorContext = Context & {
16
11
  error: any;
17
- params: any;
18
- queries: any;
19
- headers: any;
20
12
  };
21
13
  export type ErrorHandler = (ctx: ErrorContext) => Promise<void>;
@@ -6,5 +6,4 @@
6
6
  * For the full copyright and license information, please view the LICENSE
7
7
  * file that was distributed with this source code.
8
8
  */
9
- import { Request } from '#src/context/Request';
10
- import { Response } from '#src/context/Response';
9
+ export {};