@ogcio/fastify-error-handler 4.0.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.
- package/README.md +33 -0
- package/__tests__/helpers/build-fastify.ts +88 -0
- package/__tests__/helpers/fastify-test-helpers.ts +44 -0
- package/__tests__/initialize-error-handler.test.ts +99 -0
- package/__tests__/life-events-error-handler.test.ts +82 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/initialize-error-handler.d.ts +13 -0
- package/dist/initialize-error-handler.d.ts.map +1 -0
- package/dist/initialize-error-handler.js +118 -0
- package/dist/initialize-error-handler.js.map +1 -0
- package/package.json +31 -0
- package/src/index.ts +10 -0
- package/src/initialize-error-handler.ts +180 -0
- package/tsconfig.json +12 -0
- package/tsconfig.prod.json +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Fastify Error Handler
|
|
2
|
+
|
|
3
|
+
This error handler package's goal is to standardize the management of the errors across Fastify services.
|
|
4
|
+
|
|
5
|
+
## How to use it
|
|
6
|
+
|
|
7
|
+
To use this package 2 steps are needed:
|
|
8
|
+
- install the package with
|
|
9
|
+
```
|
|
10
|
+
npm i @ogcio/fastify-error-handler
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
- use the `initializeErrorHandler(server)` method to set the error handlers for the `fastify` server
|
|
14
|
+
|
|
15
|
+
That's it!
|
|
16
|
+
|
|
17
|
+
## How to raise errors
|
|
18
|
+
|
|
19
|
+
To standardize error handling in the Fastify services, the suggested way to go is to raise exceptions by using the `HttpError` errors from the [fastify-sensible](https://github.com/fastify/fastify-sensible) package.
|
|
20
|
+
|
|
21
|
+
It exposes some predefined error types
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
import { httpErrors } from "@fastify/sensible";
|
|
25
|
+
......
|
|
26
|
+
// predefined error
|
|
27
|
+
throw httpErrors.notFound("Route not found");
|
|
28
|
+
|
|
29
|
+
// custom error
|
|
30
|
+
throw httpErrors.createError(404, "message", {additional: {data: "here"}});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The `error-handler` package is ready to manage the errors, log them and transform them in the output HTTP error you need.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { FastifyError, createError } from "@fastify/error";
|
|
2
|
+
import { pino, DestinationStream } from "pino";
|
|
3
|
+
import fastify, { FastifyInstance } from "fastify";
|
|
4
|
+
import { initializeErrorHandler } from "../../src/index.js";
|
|
5
|
+
import fastifySensible from "@fastify/sensible";
|
|
6
|
+
import httpErrors from "http-errors";
|
|
7
|
+
export const buildFastify = async (
|
|
8
|
+
loggerDestination?: DestinationStream
|
|
9
|
+
): Promise<FastifyInstance> => {
|
|
10
|
+
const server = fastify({ logger: pino({}, loggerDestination) });
|
|
11
|
+
initializeErrorHandler(server as unknown as FastifyInstance);
|
|
12
|
+
await server.register(fastifySensible);
|
|
13
|
+
server.get("/error", async (request, _reply) => {
|
|
14
|
+
const parsed = request.query as { [x: string]: unknown };
|
|
15
|
+
const requestedStatusCode = Number(parsed["status_code"] ?? "500");
|
|
16
|
+
const requestedMessage = String(parsed["error_message"] ?? "WHOOOPS");
|
|
17
|
+
|
|
18
|
+
if (!parsed["status_code"]) {
|
|
19
|
+
throw new Error(requestedMessage);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
throw createError(
|
|
23
|
+
"CUSTOM_CODE",
|
|
24
|
+
requestedMessage as string,
|
|
25
|
+
requestedStatusCode as number
|
|
26
|
+
)();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
server.get("/validation", async (request, _reply) => {
|
|
30
|
+
const parsed = request.query as { [x: string]: unknown };
|
|
31
|
+
const requestedMessage = String(parsed["error_message"] ?? "WHOOOPS");
|
|
32
|
+
|
|
33
|
+
const error = createError(
|
|
34
|
+
"CUSTOM_CODE",
|
|
35
|
+
requestedMessage as string,
|
|
36
|
+
422,
|
|
37
|
+
)() as FastifyError & {
|
|
38
|
+
headers: { [x: string]: unknown };
|
|
39
|
+
status: number | undefined;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
error.validation = [
|
|
43
|
+
{
|
|
44
|
+
keyword: "field",
|
|
45
|
+
instancePath: "the.instance.path",
|
|
46
|
+
schemaPath: "the.schema.path",
|
|
47
|
+
params: { field: "one", property: "two" },
|
|
48
|
+
message: requestedMessage,
|
|
49
|
+
},
|
|
50
|
+
];
|
|
51
|
+
error.validationContext = "body";
|
|
52
|
+
error.status = 423;
|
|
53
|
+
|
|
54
|
+
throw error;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
server.get("/life-events/custom", async (request, _reply) => {
|
|
58
|
+
const parsed = request.query as { [x: string]: unknown };
|
|
59
|
+
const requestedStatusCode = Number(parsed["status_code"] ?? "500");
|
|
60
|
+
|
|
61
|
+
throw server.httpErrors.createError(
|
|
62
|
+
requestedStatusCode as number,
|
|
63
|
+
"message"
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
server.get("/life-events/validation", async (_request, _reply) => {
|
|
68
|
+
throw server.httpErrors.createError(422, "message", {
|
|
69
|
+
validationErrors: [
|
|
70
|
+
{ fieldName: "field", message: "error", validationRule: "equal" },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
server.get("/life-events/:errorCode", async (request, _reply) => {
|
|
76
|
+
const errorCode = Number((request.params! as { errorCode: string })
|
|
77
|
+
.errorCode);
|
|
78
|
+
if (!httpErrors[errorCode]) {
|
|
79
|
+
throw new Error("Wrong parameter");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const errorObj = httpErrors[errorCode];
|
|
83
|
+
|
|
84
|
+
throw new errorObj("Failed Correctly!");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return server as unknown as FastifyInstance;
|
|
88
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import { buildFastify } from "./build-fastify.js";
|
|
3
|
+
export const DEFAULT_HOSTNAME = "localhost:80";
|
|
4
|
+
export const DEFAULT_USER_AGENT = "lightMyRequest";
|
|
5
|
+
export const DEFAULT_REQUEST_HEADERS = {
|
|
6
|
+
"user-agent": "lightMyRequest",
|
|
7
|
+
host: "localhost:80",
|
|
8
|
+
};
|
|
9
|
+
export const DEFAULT_CLIENT_IP = "127.0.0.1";
|
|
10
|
+
export const DEFAULT_CONTENT_TYPE = "application/json; charset=utf-8";
|
|
11
|
+
export const DEFAULT_METHOD = "GET";
|
|
12
|
+
export const DEFAULT_SCHEME = "http";
|
|
13
|
+
export const DEFAULT_PATH = "/error";
|
|
14
|
+
|
|
15
|
+
export interface TestingLoggerDestination {
|
|
16
|
+
loggerDestination: {
|
|
17
|
+
write: (_data: string) => number;
|
|
18
|
+
};
|
|
19
|
+
getLoggedRecords: () => string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const getTestingDestinationLogger = (): TestingLoggerDestination => {
|
|
23
|
+
const testCaseRecords: string[] = [];
|
|
24
|
+
const getLoggedRecords = () => testCaseRecords;
|
|
25
|
+
const loggerDestination = {
|
|
26
|
+
write: (data: string) => testCaseRecords.push(data),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return { loggerDestination, getLoggedRecords };
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const initializeServer = async (): Promise<{
|
|
33
|
+
server: FastifyInstance;
|
|
34
|
+
loggingDestination: TestingLoggerDestination;
|
|
35
|
+
}> => {
|
|
36
|
+
const loggingDestination = getTestingDestinationLogger();
|
|
37
|
+
const server = await buildFastify(loggingDestination.loggerDestination);
|
|
38
|
+
|
|
39
|
+
return { server, loggingDestination };
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
|
+
export const parseLogEntry = (logEntry: string): { [x: string]: any } =>
|
|
44
|
+
JSON.parse(logEntry);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_METHOD,
|
|
5
|
+
DEFAULT_PATH,
|
|
6
|
+
initializeServer,
|
|
7
|
+
} from "./helpers/fastify-test-helpers.js";
|
|
8
|
+
import httpErrors from "http-errors";
|
|
9
|
+
import { HttpErrorClasses } from "@ogcio/shared-errors";
|
|
10
|
+
|
|
11
|
+
test("Common error is managed as expected", async (t) => {
|
|
12
|
+
const { server } = await initializeServer();
|
|
13
|
+
t.after(() => server.close());
|
|
14
|
+
|
|
15
|
+
const response = await server.inject({
|
|
16
|
+
method: DEFAULT_METHOD,
|
|
17
|
+
url: DEFAULT_PATH,
|
|
18
|
+
query: { status_code: "500", error_message: "error message" },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
assert.ok(typeof response !== "undefined");
|
|
22
|
+
assert.equal(response?.statusCode, 500);
|
|
23
|
+
assert.deepStrictEqual(response.json(), {
|
|
24
|
+
code: HttpErrorClasses.ServerError,
|
|
25
|
+
detail: "error message",
|
|
26
|
+
requestId: "req-1",
|
|
27
|
+
name: "FastifyError",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("Validation error is managed as a Life Events One", async (t) => {
|
|
32
|
+
const { server } = await initializeServer();
|
|
33
|
+
t.after(() => server.close());
|
|
34
|
+
|
|
35
|
+
const response = await server.inject({
|
|
36
|
+
method: DEFAULT_METHOD,
|
|
37
|
+
url: "/validation",
|
|
38
|
+
query: { error_message: "error message" },
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
assert.ok(typeof response !== "undefined");
|
|
42
|
+
assert.equal(response?.statusCode, 422);
|
|
43
|
+
assert.deepStrictEqual(response.json(), {
|
|
44
|
+
code: HttpErrorClasses.ValidationError,
|
|
45
|
+
detail: "error message",
|
|
46
|
+
requestId: "req-1",
|
|
47
|
+
name: "UnprocessableEntityError",
|
|
48
|
+
validation: [
|
|
49
|
+
{
|
|
50
|
+
fieldName: "the.instance.path",
|
|
51
|
+
message: "error message",
|
|
52
|
+
validationRule: "field",
|
|
53
|
+
additionalInfo: {
|
|
54
|
+
field: "one",
|
|
55
|
+
property: "two",
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("If an error with status 200 is raised, it is managed as an unknown one", async (t) => {
|
|
63
|
+
const { server } = await initializeServer();
|
|
64
|
+
t.after(() => server.close());
|
|
65
|
+
|
|
66
|
+
const response = await server.inject({
|
|
67
|
+
method: DEFAULT_METHOD,
|
|
68
|
+
url: DEFAULT_PATH,
|
|
69
|
+
query: { status_code: "200", error_message: "error message" },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
assert.ok(typeof response !== "undefined");
|
|
73
|
+
assert.equal(response?.statusCode, 500);
|
|
74
|
+
assert.deepStrictEqual(response.json(), {
|
|
75
|
+
code: HttpErrorClasses.UnknownError,
|
|
76
|
+
detail: "error message",
|
|
77
|
+
requestId: "req-1",
|
|
78
|
+
name: "FastifyError",
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("404 error is managed as expected", async (t) => {
|
|
83
|
+
const { server } = await initializeServer();
|
|
84
|
+
t.after(() => server.close());
|
|
85
|
+
|
|
86
|
+
const response = await server.inject({
|
|
87
|
+
method: DEFAULT_METHOD,
|
|
88
|
+
url: "/this-path-does-not-exist",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
assert.ok(typeof response !== "undefined");
|
|
92
|
+
assert.equal(response?.statusCode, 404);
|
|
93
|
+
assert.deepStrictEqual(response.json(), {
|
|
94
|
+
code: HttpErrorClasses.NotFoundError,
|
|
95
|
+
detail: "Route not found: /this-path-does-not-exist",
|
|
96
|
+
requestId: "req-1",
|
|
97
|
+
name: new httpErrors[404]("TEMP").name,
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {
|
|
4
|
+
DEFAULT_METHOD,
|
|
5
|
+
initializeServer,
|
|
6
|
+
} from "./helpers/fastify-test-helpers.js";
|
|
7
|
+
import httpErrors from "http-errors";
|
|
8
|
+
import * as sharedErrors from "@ogcio/shared-errors";
|
|
9
|
+
|
|
10
|
+
const errorsProvider = [
|
|
11
|
+
{ errorType: httpErrors[401], expectedStatusCode: 401 },
|
|
12
|
+
{ errorType: httpErrors[403], expectedStatusCode: 403 },
|
|
13
|
+
{ errorType: httpErrors[500], expectedStatusCode: 500 },
|
|
14
|
+
{ errorType: httpErrors[404], expectedStatusCode: 404 },
|
|
15
|
+
{ errorType: httpErrors[500], expectedStatusCode: 500 },
|
|
16
|
+
{ errorType: httpErrors[502], expectedStatusCode: 502 },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
errorsProvider.forEach((errorProv) =>
|
|
20
|
+
test(`Error is managed in the correct way - ${errorProv.errorType.name}`, async (t) => {
|
|
21
|
+
const { server } = await initializeServer();
|
|
22
|
+
t.after(() => server.close());
|
|
23
|
+
|
|
24
|
+
const errorInstance = new errorProv.errorType("message");
|
|
25
|
+
|
|
26
|
+
const response = await server.inject({
|
|
27
|
+
method: DEFAULT_METHOD,
|
|
28
|
+
url: `/life-events/${errorProv.expectedStatusCode}`,
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
assert.ok(typeof response !== "undefined");
|
|
32
|
+
assert.equal(response?.statusCode, errorProv.expectedStatusCode);
|
|
33
|
+
assert.deepStrictEqual(response.json(), {
|
|
34
|
+
code: sharedErrors.parseHttpErrorClass(errorProv.expectedStatusCode),
|
|
35
|
+
detail: "Failed Correctly!",
|
|
36
|
+
requestId: "req-1",
|
|
37
|
+
name: errorInstance.name,
|
|
38
|
+
});
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
test(`Custom error is managed based on parameters`, async (t) => {
|
|
43
|
+
const { server } = await initializeServer();
|
|
44
|
+
t.after(() => server.close());
|
|
45
|
+
|
|
46
|
+
const response = await server.inject({
|
|
47
|
+
method: DEFAULT_METHOD,
|
|
48
|
+
url: `/life-events/custom`,
|
|
49
|
+
query: { status_code: "503" },
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
assert.ok(typeof response !== "undefined");
|
|
53
|
+
assert.equal(response?.statusCode, 503);
|
|
54
|
+
assert.deepStrictEqual(response.json(), {
|
|
55
|
+
code: sharedErrors.parseHttpErrorClass(503),
|
|
56
|
+
detail: "message",
|
|
57
|
+
requestId: "req-1",
|
|
58
|
+
name: new httpErrors[503]("MOCK").name,
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test(`Validation error is managed as expected`, async (t) => {
|
|
63
|
+
const { server } = await initializeServer();
|
|
64
|
+
t.after(() => server.close());
|
|
65
|
+
|
|
66
|
+
const response = await server.inject({
|
|
67
|
+
method: DEFAULT_METHOD,
|
|
68
|
+
url: `/life-events/validation`,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
assert.ok(typeof response !== "undefined");
|
|
72
|
+
assert.equal(response?.statusCode, 422);
|
|
73
|
+
assert.deepStrictEqual(response.json(), {
|
|
74
|
+
code: sharedErrors.parseHttpErrorClass(422),
|
|
75
|
+
detail: "message",
|
|
76
|
+
requestId: "req-1",
|
|
77
|
+
name: new httpErrors[422]("MOCK").name,
|
|
78
|
+
validation: [
|
|
79
|
+
{ fieldName: "field", message: "error", validationRule: "equal" },
|
|
80
|
+
],
|
|
81
|
+
});
|
|
82
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAM1C,eAAO,MAAM,sBAAsB,WAAY,eAAe,KAAG,IAGhE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EAClB,MAAM,+BAA+B,CAAC;AAEvC,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,MAAuB,EAAQ,EAAE;IACtE,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAC1B,yBAAyB,CAAC,MAAM,CAAC,CAAC;AACpC,CAAC,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import { HttpErrorClasses, ValidationErrorData } from "@ogcio/shared-errors";
|
|
3
|
+
export interface OutputHttpError {
|
|
4
|
+
code: HttpErrorClasses;
|
|
5
|
+
detail: string;
|
|
6
|
+
requestId: string;
|
|
7
|
+
name: string;
|
|
8
|
+
validation?: ValidationErrorData[];
|
|
9
|
+
process?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const setupErrorHandler: (server: FastifyInstance) => void;
|
|
12
|
+
export declare const initializeNotFoundHandler: (server: FastifyInstance) => void;
|
|
13
|
+
//# sourceMappingURL=initialize-error-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initialize-error-handler.d.ts","sourceRoot":"","sources":["../src/initialize-error-handler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,eAAe,EAEhB,MAAM,SAAS,CAAC;AAOjB,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EAEpB,MAAM,sBAAsB,CAAC;AAI9B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAOD,eAAO,MAAM,iBAAiB,WAAY,eAAe,KAAG,IAyC3D,CAAC;AAIF,eAAO,MAAM,yBAAyB,WAAY,eAAe,KAAG,IAUnE,CAAC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { setLoggingContext, getLoggingContextError, LogMessages, } from "@ogcio/fastify-logging-wrapper";
|
|
2
|
+
import { parseHttpErrorClass, } from "@ogcio/shared-errors";
|
|
3
|
+
import { isHttpError } from "http-errors";
|
|
4
|
+
import { httpErrors } from "@fastify/sensible";
|
|
5
|
+
// The error handler below is the same as the original one in Fastify,
|
|
6
|
+
// just without unwanted log entries
|
|
7
|
+
// I've opened an issue to fastify to ask them if we could avoid logging
|
|
8
|
+
// those entries when disableRequestLogging is true
|
|
9
|
+
// https://github.com/fastify/fastify/issues/5409
|
|
10
|
+
export const setupErrorHandler = (server) => {
|
|
11
|
+
const setErrorHeaders = (error, reply) => {
|
|
12
|
+
const res = reply.raw;
|
|
13
|
+
let statusCode = res.statusCode;
|
|
14
|
+
statusCode = statusCode >= 400 ? statusCode : 500;
|
|
15
|
+
// treat undefined and null as same
|
|
16
|
+
if (error != null) {
|
|
17
|
+
if (error.headers !== undefined) {
|
|
18
|
+
reply.headers(error.headers);
|
|
19
|
+
}
|
|
20
|
+
if (error.status && error.status >= 400) {
|
|
21
|
+
statusCode = error.status;
|
|
22
|
+
}
|
|
23
|
+
else if (error.statusCode && error.statusCode >= 400) {
|
|
24
|
+
statusCode = error.statusCode;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
res.statusCode = statusCode;
|
|
28
|
+
reply.statusCode = res.statusCode;
|
|
29
|
+
};
|
|
30
|
+
server.setErrorHandler(function (error, request, reply) {
|
|
31
|
+
if (isHttpError(error)) {
|
|
32
|
+
manageHttpError(error, request, reply);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
if (error.validation) {
|
|
36
|
+
const httpError = toOutputHttpValidationError(error);
|
|
37
|
+
manageHttpError(httpError, request, reply);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
setErrorHeaders(error, reply);
|
|
41
|
+
reply.send(getResponseFromFastifyError(error, request));
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
// The error handler below is the same as the original one in Fastify,
|
|
45
|
+
// just without unwanted log entries
|
|
46
|
+
export const initializeNotFoundHandler = (server) => {
|
|
47
|
+
server.setNotFoundHandler((request, reply) => {
|
|
48
|
+
const error = httpErrors.notFound(`Route not found: ${request.url}`);
|
|
49
|
+
setLoggingContext({
|
|
50
|
+
error,
|
|
51
|
+
});
|
|
52
|
+
request.log.error({ error: getLoggingContextError() }, LogMessages.Error);
|
|
53
|
+
manageHttpError(error, request, reply);
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
const getValidationFromFastifyError = (validationInput) => {
|
|
57
|
+
const output = [];
|
|
58
|
+
for (const input of validationInput) {
|
|
59
|
+
const key = input.params?.missingProperty ?? input.instancePath.split("/").pop();
|
|
60
|
+
const message = input.message ?? input.keyword;
|
|
61
|
+
if (key && typeof key === "string") {
|
|
62
|
+
output.push({
|
|
63
|
+
fieldName: key,
|
|
64
|
+
message,
|
|
65
|
+
validationRule: input.keyword,
|
|
66
|
+
additionalInfo: input.params,
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
output.push({
|
|
71
|
+
fieldName: input.schemaPath,
|
|
72
|
+
message,
|
|
73
|
+
validationRule: input.keyword,
|
|
74
|
+
additionalInfo: input.params,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return { validation: output };
|
|
78
|
+
};
|
|
79
|
+
const getResponseFromFastifyError = (error, request) => {
|
|
80
|
+
const output = {
|
|
81
|
+
code: parseHttpErrorClass(error.statusCode),
|
|
82
|
+
detail: error.message,
|
|
83
|
+
requestId: request.id,
|
|
84
|
+
name: error.name,
|
|
85
|
+
};
|
|
86
|
+
if (error.validation && error.validation.length > 0) {
|
|
87
|
+
output.validation = getValidationFromFastifyError(error.validation).validation;
|
|
88
|
+
}
|
|
89
|
+
return output;
|
|
90
|
+
};
|
|
91
|
+
const manageHttpError = (error, request, reply) => {
|
|
92
|
+
reply.raw.statusCode = error.statusCode;
|
|
93
|
+
reply.statusCode = error.statusCode;
|
|
94
|
+
const errorResponse = {
|
|
95
|
+
code: parseHttpErrorClass(error.statusCode),
|
|
96
|
+
detail: error.message,
|
|
97
|
+
requestId: request.id,
|
|
98
|
+
name: error.name,
|
|
99
|
+
process: error.errorProcess,
|
|
100
|
+
};
|
|
101
|
+
let validationErrors = error.validationErrors && error.validationErrors.length > 0
|
|
102
|
+
? error.validationErrors
|
|
103
|
+
: undefined;
|
|
104
|
+
if (!validationErrors && error.validation && error.validation.length > 0) {
|
|
105
|
+
validationErrors = error.validation;
|
|
106
|
+
}
|
|
107
|
+
if (validationErrors) {
|
|
108
|
+
errorResponse.validation = validationErrors;
|
|
109
|
+
}
|
|
110
|
+
reply.status(error.statusCode).send(errorResponse);
|
|
111
|
+
};
|
|
112
|
+
const toOutputHttpValidationError = (error) => {
|
|
113
|
+
if (!error.validation) {
|
|
114
|
+
throw httpErrors.createError(500, "This is not a validation error");
|
|
115
|
+
}
|
|
116
|
+
return httpErrors.createError(422, error.message, getValidationFromFastifyError(error.validation));
|
|
117
|
+
};
|
|
118
|
+
//# sourceMappingURL=initialize-error-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"initialize-error-handler.js","sourceRoot":"","sources":["../src/initialize-error-handler.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ogcio/fastify-error-handler",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "rm -rf dist tsconfig.prod.tsbuildinfo tsconfig.tsbuildinfo && tsc -p tsconfig.prod.json",
|
|
9
|
+
"test": "tap --jobs=1 --allow-incomplete-coverage __tests__/**/*.test.ts"
|
|
10
|
+
},
|
|
11
|
+
"keywords": [],
|
|
12
|
+
"author": {
|
|
13
|
+
"name": "Samuele Salvatico",
|
|
14
|
+
"email": "samuele.salvatico@nearform.com"
|
|
15
|
+
},
|
|
16
|
+
"license": "ISC",
|
|
17
|
+
"description": "Normalize the error handling of errors with related logs",
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@fastify/sensible": "^5.6.0",
|
|
20
|
+
"@ogcio/fastify-logging-wrapper": "^4.0.0",
|
|
21
|
+
"fastify": "^4.28.1"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@types/http-errors": "^2.0.4"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/ogcio/shared-node-utils.git",
|
|
29
|
+
"directory": "packages/fastify-error-handler"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FastifyInstance } from "fastify";
|
|
2
|
+
import {
|
|
3
|
+
initializeNotFoundHandler,
|
|
4
|
+
setupErrorHandler
|
|
5
|
+
} from "./initialize-error-handler.js";
|
|
6
|
+
|
|
7
|
+
export const initializeErrorHandler = (server: FastifyInstance): void => {
|
|
8
|
+
setupErrorHandler(server);
|
|
9
|
+
initializeNotFoundHandler(server);
|
|
10
|
+
};
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FastifyError,
|
|
3
|
+
FastifyRequest,
|
|
4
|
+
FastifyInstance,
|
|
5
|
+
FastifyReply,
|
|
6
|
+
} from "fastify";
|
|
7
|
+
import { FastifySchemaValidationError } from "fastify/types/schema.js";
|
|
8
|
+
import {
|
|
9
|
+
setLoggingContext,
|
|
10
|
+
getLoggingContextError,
|
|
11
|
+
LogMessages,
|
|
12
|
+
} from "@ogcio/fastify-logging-wrapper";
|
|
13
|
+
import {
|
|
14
|
+
HttpErrorClasses,
|
|
15
|
+
ValidationErrorData,
|
|
16
|
+
parseHttpErrorClass,
|
|
17
|
+
} from "@ogcio/shared-errors";
|
|
18
|
+
import { isHttpError } from "http-errors";
|
|
19
|
+
import { HttpError, httpErrors } from "@fastify/sensible";
|
|
20
|
+
|
|
21
|
+
export interface OutputHttpError {
|
|
22
|
+
code: HttpErrorClasses;
|
|
23
|
+
detail: string;
|
|
24
|
+
requestId: string;
|
|
25
|
+
name: string;
|
|
26
|
+
validation?: ValidationErrorData[];
|
|
27
|
+
process?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// The error handler below is the same as the original one in Fastify,
|
|
31
|
+
// just without unwanted log entries
|
|
32
|
+
// I've opened an issue to fastify to ask them if we could avoid logging
|
|
33
|
+
// those entries when disableRequestLogging is true
|
|
34
|
+
// https://github.com/fastify/fastify/issues/5409
|
|
35
|
+
export const setupErrorHandler = (server: FastifyInstance): void => {
|
|
36
|
+
const setErrorHeaders = (
|
|
37
|
+
error: null | {
|
|
38
|
+
headers?: { [x: string]: string | number | string[] | undefined };
|
|
39
|
+
status?: number;
|
|
40
|
+
statusCode?: number;
|
|
41
|
+
},
|
|
42
|
+
reply: FastifyReply
|
|
43
|
+
) => {
|
|
44
|
+
const res = reply.raw;
|
|
45
|
+
let statusCode = res.statusCode;
|
|
46
|
+
statusCode = statusCode >= 400 ? statusCode : 500;
|
|
47
|
+
// treat undefined and null as same
|
|
48
|
+
if (error != null) {
|
|
49
|
+
if (error.headers !== undefined) {
|
|
50
|
+
reply.headers(error.headers);
|
|
51
|
+
}
|
|
52
|
+
if (error.status && error.status >= 400) {
|
|
53
|
+
statusCode = error.status;
|
|
54
|
+
} else if (error.statusCode && error.statusCode >= 400) {
|
|
55
|
+
statusCode = error.statusCode;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
res.statusCode = statusCode;
|
|
59
|
+
reply.statusCode = res.statusCode;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
server.setErrorHandler(function (error, request, reply) {
|
|
63
|
+
if (isHttpError(error)) {
|
|
64
|
+
manageHttpError(error, request, reply);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (error.validation) {
|
|
68
|
+
const httpError = toOutputHttpValidationError(error);
|
|
69
|
+
manageHttpError(httpError, request, reply);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
setErrorHeaders(error, reply);
|
|
74
|
+
reply.send(getResponseFromFastifyError(error, request));
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// The error handler below is the same as the original one in Fastify,
|
|
79
|
+
// just without unwanted log entries
|
|
80
|
+
export const initializeNotFoundHandler = (server: FastifyInstance): void => {
|
|
81
|
+
server.setNotFoundHandler((request: FastifyRequest, reply: FastifyReply) => {
|
|
82
|
+
const error = httpErrors.notFound(`Route not found: ${request.url}`);
|
|
83
|
+
setLoggingContext({
|
|
84
|
+
error,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
request.log.error({ error: getLoggingContextError() }, LogMessages.Error);
|
|
88
|
+
manageHttpError(error, request, reply);
|
|
89
|
+
});
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const getValidationFromFastifyError = (
|
|
93
|
+
validationInput: FastifySchemaValidationError[]
|
|
94
|
+
): { validation: ValidationErrorData[] } => {
|
|
95
|
+
const output: ValidationErrorData[] = [];
|
|
96
|
+
|
|
97
|
+
for (const input of validationInput) {
|
|
98
|
+
const key =
|
|
99
|
+
input.params?.missingProperty ?? input.instancePath.split("/").pop();
|
|
100
|
+
const message = input.message ?? input.keyword;
|
|
101
|
+
if (key && typeof key === "string") {
|
|
102
|
+
output.push({
|
|
103
|
+
fieldName: key,
|
|
104
|
+
message,
|
|
105
|
+
validationRule: input.keyword,
|
|
106
|
+
additionalInfo: input.params,
|
|
107
|
+
});
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
output.push({
|
|
112
|
+
fieldName: input.schemaPath,
|
|
113
|
+
message,
|
|
114
|
+
validationRule: input.keyword,
|
|
115
|
+
additionalInfo: input.params,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return { validation: output };
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const getResponseFromFastifyError = (
|
|
123
|
+
error: FastifyError,
|
|
124
|
+
request: FastifyRequest
|
|
125
|
+
): OutputHttpError => {
|
|
126
|
+
const output: OutputHttpError = {
|
|
127
|
+
code: parseHttpErrorClass(error.statusCode),
|
|
128
|
+
detail: error.message,
|
|
129
|
+
requestId: request.id,
|
|
130
|
+
name: error.name,
|
|
131
|
+
};
|
|
132
|
+
if (error.validation && error.validation.length > 0) {
|
|
133
|
+
output.validation = getValidationFromFastifyError(
|
|
134
|
+
error.validation
|
|
135
|
+
).validation;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return output;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const manageHttpError = (
|
|
142
|
+
error: HttpError,
|
|
143
|
+
request: FastifyRequest,
|
|
144
|
+
reply: FastifyReply
|
|
145
|
+
): void => {
|
|
146
|
+
reply.raw.statusCode = error.statusCode;
|
|
147
|
+
reply.statusCode = error.statusCode;
|
|
148
|
+
const errorResponse: OutputHttpError = {
|
|
149
|
+
code: parseHttpErrorClass(error.statusCode),
|
|
150
|
+
detail: error.message,
|
|
151
|
+
requestId: request.id,
|
|
152
|
+
name: error.name,
|
|
153
|
+
process: error.errorProcess,
|
|
154
|
+
};
|
|
155
|
+
let validationErrors =
|
|
156
|
+
error.validationErrors && error.validationErrors.length > 0
|
|
157
|
+
? error.validationErrors
|
|
158
|
+
: undefined;
|
|
159
|
+
if (!validationErrors && error.validation && error.validation.length > 0) {
|
|
160
|
+
validationErrors = error.validation;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (validationErrors) {
|
|
164
|
+
errorResponse.validation = validationErrors;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
reply.status(error.statusCode).send(errorResponse);
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const toOutputHttpValidationError = (error: FastifyError): HttpError => {
|
|
171
|
+
if (!error.validation) {
|
|
172
|
+
throw httpErrors.createError(500, "This is not a validation error");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return httpErrors.createError(
|
|
176
|
+
422,
|
|
177
|
+
error.message,
|
|
178
|
+
getValidationFromFastifyError(error.validation)
|
|
179
|
+
);
|
|
180
|
+
};
|
package/tsconfig.json
ADDED