Skip to content

Commit 4e62858

Browse files
authored
Feat/len set error handler (#17)
* feat: add controller and request mappers decorators * refactor: remove route array from Http class * refactor: create handler to hook preHandler * feat: implement fastify handler class and set error handler method * style: add more keywords in package.json
1 parent 70118b1 commit 4e62858

File tree

9 files changed

+141
-90
lines changed

9 files changed

+141
-90
lines changed

package.json

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@secjs/http",
3-
"version": "1.0.2",
3+
"version": "1.0.3",
44
"description": "",
55
"license": "MIT",
66
"author": "João Lenon",
@@ -15,6 +15,10 @@
1515
},
1616
"keywords": [
1717
"http",
18+
"handler",
19+
"router",
20+
"context",
21+
"middleware",
1822
"fastify",
1923
"nodejs"
2024
],

src/Context/Request.ts

+1-5
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ export class Request implements RequestContract {
3030
}
3131

3232
get baseUrl(): string {
33-
return this.request.url
34-
}
35-
36-
get originalUrl(): string {
37-
return this.request.url
33+
return this.request.url.split('?')[0]
3834
}
3935

4036
get body(): Record<string, any> {

src/Contracts/Context/ContextContract.ts

+1
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ export interface ContextContract {
1818
queries: Record<string, string>
1919
next?: NextContract
2020
data?: Record<string, any>
21+
error?: any
2122
}

src/Contracts/Context/HandlerContract.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
import { ContextContract } from './ContextContract'
1111

1212
export interface HandlerContract {
13-
(ctx: ContextContract): Promise<any> | any
13+
(ctx?: ContextContract): Promise<any> | any
1414
}

src/Contracts/Context/RequestContract.ts

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export interface RequestContract {
1212
method: string
1313
fullUrl: string
1414
baseUrl: string
15-
originalUrl: string
1615
body: Record<string, any>
1716
params: Record<string, string>
1817
queries: Record<string, string>

src/Http.ts

+12-81
Original file line numberDiff line numberDiff line change
@@ -7,79 +7,25 @@
77
* file that was distributed with this source code.
88
*/
99

10-
import fastify, {
11-
FastifyInstance,
12-
FastifyReply,
13-
FastifyRequest,
14-
PrintRoutesOptions,
15-
} from 'fastify'
16-
17-
import { String } from '@secjs/utils'
18-
import { Request } from './Context/Request'
19-
import { Response } from './Context/Response'
10+
import fastify, { FastifyInstance, PrintRoutesOptions } from 'fastify'
11+
12+
import { FastifyHandler } from './Utils/FastifyHandler'
2013
import { HttpMethodTypes } from './Contracts/HttpMethodTypes'
14+
import { defaultErrorHandler } from './Utils/defaultErrorHandler'
2115
import { HandlerContract } from './Contracts/Context/HandlerContract'
22-
import { FastifyHandlerContract } from './Contracts/FastifyHandlerContract'
23-
24-
declare module 'fastify' {
25-
interface FastifyRequest {
26-
data: Record<string, any>
27-
}
28-
}
2916

3017
export class Http {
3118
private readonly server: FastifyInstance
3219

3320
constructor() {
3421
this.server = fastify()
35-
this.server.setErrorHandler(Http.defaultErrorHandler)
22+
this.setErrorHandler(defaultErrorHandler)
3623
}
3724

38-
private static defaultErrorHandler(
39-
error: any,
40-
request: FastifyRequest,
41-
reply: FastifyReply,
42-
) {
43-
const code = error.code || error.name
44-
const statusCode = error.statusCode || error.status || 500
45-
46-
const body = {
47-
code: String.toSnakeCase(code).toUpperCase(),
48-
path: request.url,
49-
method: request.method,
50-
status: statusCode <= 399 ? 'SUCCESS' : 'ERROR',
51-
statusCode: statusCode,
52-
error: {
53-
name: error.name,
54-
message: error.message,
55-
stack: error.stack,
56-
},
57-
}
58-
59-
reply.status(statusCode).send(body)
60-
}
25+
setErrorHandler(handler: HandlerContract) {
26+
const fastifyErrorHandler = FastifyHandler.createErrorHandler(handler)
6127

62-
private createFastifyHandler(
63-
handler: (ctx) => Promise<void> | void,
64-
): FastifyHandlerContract {
65-
return async (req: FastifyRequest, res: FastifyReply) => {
66-
const request = new Request(req)
67-
const response = new Response(res)
68-
69-
if (!req.data) req.data = {}
70-
if (!req.query) req.query = {}
71-
if (!req.params) req.params = {}
72-
73-
return handler({
74-
request,
75-
response,
76-
params: req.params,
77-
queries: req.query,
78-
data: req.data,
79-
// eslint-disable-next-line @typescript-eslint/no-empty-function
80-
next: () => {},
81-
})
82-
}
28+
this.server.setErrorHandler(fastifyErrorHandler)
8329
}
8430

8531
getServer(): FastifyInstance {
@@ -91,23 +37,7 @@ export class Http {
9137
}
9238

9339
use(handler: HandlerContract) {
94-
this.server.addHook('preHandler', (req, res, done) => {
95-
const request = new Request(req)
96-
const response = new Response(res)
97-
98-
if (!req.data) req.data = {}
99-
if (!req.query) req.query = {}
100-
if (!req.params) req.params = {}
101-
102-
return handler({
103-
request,
104-
response,
105-
params: req.params as Record<string, string>,
106-
queries: req.query as Record<string, string>,
107-
data: req.data,
108-
next: done,
109-
})
110-
})
40+
this.server.addHook('preHandler', FastifyHandler.createPreHandler(handler))
11141
}
11242

11343
listen(
@@ -130,9 +60,10 @@ export class Http {
13060
this.server.route({
13161
url,
13262
method: methods,
133-
handler: this.createFastifyHandler(handler),
63+
handler: FastifyHandler.createRequestHandler(handler),
13464
preHandler:
135-
middlewares && middlewares.map(mid => this.createFastifyHandler(mid)),
65+
middlewares &&
66+
middlewares.map(mid => FastifyHandler.createPreHandler(mid)),
13667
})
13768
}
13869

src/Utils/FastifyHandler.ts

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/**
2+
* @secjs/http
3+
*
4+
* (c) João Lenon <lenon@secjs.com.br>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
/* eslint-disable @typescript-eslint/no-empty-function */
11+
12+
import { Request } from '../Context/Request'
13+
import { Response } from '../Context/Response'
14+
import { HandlerContract } from '../Contracts/Context/HandlerContract'
15+
import { FastifyReply, FastifyRequest } from 'fastify'
16+
17+
declare module 'fastify' {
18+
interface FastifyRequest {
19+
data: Record<string, any>
20+
}
21+
}
22+
23+
export class FastifyHandler {
24+
static createPreHandler(handler: HandlerContract) {
25+
return (req, res, done) => {
26+
const request = new Request(req)
27+
const response = new Response(res)
28+
29+
if (!req.data) req.data = {}
30+
if (!req.query) req.query = {}
31+
if (!req.params) req.params = {}
32+
33+
return handler({
34+
request,
35+
response,
36+
params: req.params as Record<string, string>,
37+
queries: req.query as Record<string, string>,
38+
data: req.data,
39+
next: done,
40+
})
41+
}
42+
}
43+
44+
static createErrorHandler(handler: HandlerContract) {
45+
return (error: any, req: FastifyRequest, res: FastifyReply) => {
46+
const request = new Request(req)
47+
const response = new Response(res)
48+
49+
if (!req.data) req.data = {}
50+
if (!req.query) req.query = {}
51+
if (!req.params) req.params = {}
52+
53+
return handler({
54+
request,
55+
response,
56+
params: req.params as Record<string, string>,
57+
queries: req.query as Record<string, string>,
58+
data: req.data,
59+
next: () => {},
60+
error,
61+
})
62+
}
63+
}
64+
65+
static createRequestHandler(handler: HandlerContract) {
66+
return async (req: FastifyRequest, res: FastifyReply) => {
67+
const request = new Request(req)
68+
const response = new Response(res)
69+
70+
if (!req.data) req.data = {}
71+
if (!req.query) req.query = {}
72+
if (!req.params) req.params = {}
73+
74+
return handler({
75+
request,
76+
response,
77+
params: req.params as Record<string, string>,
78+
queries: req.query as Record<string, string>,
79+
data: req.data,
80+
// eslint-disable-next-line @typescript-eslint/no-empty-function
81+
next: () => {},
82+
})
83+
}
84+
}
85+
}

src/Utils/defaultErrorHandler.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @secjs/http
3+
*
4+
* (c) João Lenon <lenon@secjs.com.br>
5+
*
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
import { String } from '@secjs/utils'
11+
import { ContextContract } from '../Contracts/Context/ContextContract'
12+
13+
export function defaultErrorHandler({
14+
error,
15+
request,
16+
response,
17+
}: ContextContract) {
18+
const code = error.code || error.name
19+
const statusCode = error.statusCode || error.status || 500
20+
21+
const body = {
22+
code: String.toSnakeCase(code).toUpperCase(),
23+
path: request.baseUrl,
24+
method: request.method,
25+
status: statusCode <= 399 ? 'SUCCESS' : 'ERROR',
26+
statusCode: statusCode,
27+
error: {
28+
name: error.name,
29+
message: error.message,
30+
stack: error.stack,
31+
},
32+
}
33+
34+
response.status(statusCode).send(body)
35+
}

tests/http.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ describe('\n Http Class', () => {
8787

8888
expect(response.status).toBe(400)
8989
expect(response.body.code).toStrictEqual('BAD_REQUEST_EXCEPTION')
90-
expect(response.body.path).toStrictEqual('/test?throwError=true')
90+
expect(response.body.path).toStrictEqual('/test')
9191
expect(response.body.method).toStrictEqual('GET')
9292
expect(response.body.status).toStrictEqual('ERROR')
9393
expect(response.body.statusCode).toStrictEqual(400)

0 commit comments

Comments
 (0)