@divine-lab/request 1.0.1
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/LICENSE +10 -0
- package/dist/errors/APIError.d.ts +26 -0
- package/dist/errors/APIError.js +40 -0
- package/dist/errors/BackendErrors.d.ts +63 -0
- package/dist/errors/BackendErrors.js +58 -0
- package/dist/errors/index.d.ts +2 -0
- package/dist/errors/index.js +18 -0
- package/dist/fastify-helpers/index.d.ts +90 -0
- package/dist/fastify-helpers/index.js +107 -0
- package/dist/request.d.ts +1 -0
- package/dist/request.js +2 -0
- package/package.json +46 -0
- package/readme.md +118 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
All Rights Reserved
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Divine Lab
|
|
4
|
+
|
|
5
|
+
Permission to view and use this software for personal,
|
|
6
|
+
non-commercial purposes is granted.
|
|
7
|
+
|
|
8
|
+
Any commercial use, redistribution, modification,
|
|
9
|
+
sublicensing, or publication requires explicit written
|
|
10
|
+
permission from the author.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ServerErrorOptions, type ServerErrorKey } from "../errors/BackendErrors";
|
|
2
|
+
/** Custom API Error class for standardized error handling
|
|
3
|
+
* @extends Error
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* throw new APIError("NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
7
|
+
* ```
|
|
8
|
+
*/
|
|
9
|
+
export declare class APIError extends Error {
|
|
10
|
+
readonly error: ServerErrorKey;
|
|
11
|
+
readonly status: number;
|
|
12
|
+
readonly title: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
readonly type: string;
|
|
15
|
+
readonly data: any;
|
|
16
|
+
/**
|
|
17
|
+
* Creates an instance of APIError.
|
|
18
|
+
* @param error - error code or identifier from SERVER_ERRORS
|
|
19
|
+
* @param options - additional error details (title, detail, type, data)
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* throw new APIError("NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
constructor(error: ServerErrorKey, { title, detail, type, data, status }?: ServerErrorOptions);
|
|
26
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.APIError = void 0;
|
|
4
|
+
const BackendErrors_1 = require("../errors/BackendErrors");
|
|
5
|
+
/** Custom API Error class for standardized error handling
|
|
6
|
+
* @extends Error
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* throw new APIError("NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
class APIError extends Error {
|
|
13
|
+
error;
|
|
14
|
+
status;
|
|
15
|
+
title;
|
|
16
|
+
detail;
|
|
17
|
+
type;
|
|
18
|
+
data;
|
|
19
|
+
/**
|
|
20
|
+
* Creates an instance of APIError.
|
|
21
|
+
* @param error - error code or identifier from SERVER_ERRORS
|
|
22
|
+
* @param options - additional error details (title, detail, type, data)
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* throw new APIError("NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
constructor(error, { title, detail, type, data, status } = {}) {
|
|
29
|
+
const errorDef = BackendErrors_1.SERVER_ERRORS[error];
|
|
30
|
+
super(title);
|
|
31
|
+
this.error = error;
|
|
32
|
+
this.status = status || errorDef.status;
|
|
33
|
+
this.title = title || errorDef.title;
|
|
34
|
+
this.detail = detail || errorDef.detail;
|
|
35
|
+
this.type = type || errorDef.type;
|
|
36
|
+
this.data = data || null;
|
|
37
|
+
Object.setPrototypeOf(this, APIError.prototype);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.APIError = APIError;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Defines standardized server error responses based on RFC 9457.
|
|
2
|
+
* Each error includes a status code, type, title, and detail message.
|
|
3
|
+
* These can be used throughout the application for consistent error handling and responses.
|
|
4
|
+
* Additional errors can be added to the SERVER_ERRORS object as needed.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SERVER_ERRORS: {
|
|
7
|
+
readonly INTERNAL_SERVER_ERROR: {
|
|
8
|
+
readonly status: 500;
|
|
9
|
+
readonly type: "about:blank";
|
|
10
|
+
readonly title: "Internal Server Error";
|
|
11
|
+
readonly detail: "An unexpected error occurred on the server.";
|
|
12
|
+
};
|
|
13
|
+
readonly SERVICE_UNAVAILABLE: {
|
|
14
|
+
readonly status: 503;
|
|
15
|
+
readonly type: "about:blank";
|
|
16
|
+
readonly title: "Service Unavailable";
|
|
17
|
+
readonly detail: "The server is currently unable to handle the request due to temporary overload or maintenance.";
|
|
18
|
+
};
|
|
19
|
+
readonly UNKNOWN_ERROR: {
|
|
20
|
+
readonly status: 520;
|
|
21
|
+
readonly type: "about:blank";
|
|
22
|
+
readonly title: "Unknown Error";
|
|
23
|
+
readonly detail: "An unknown error occurred.";
|
|
24
|
+
};
|
|
25
|
+
readonly BAD_REQUEST: {
|
|
26
|
+
readonly status: 400;
|
|
27
|
+
readonly type: "about:blank";
|
|
28
|
+
readonly title: "Bad Request";
|
|
29
|
+
readonly detail: "The server could not understand the request due to invalid syntax.";
|
|
30
|
+
};
|
|
31
|
+
readonly UNAUTHORIZED: {
|
|
32
|
+
readonly status: 401;
|
|
33
|
+
readonly type: "about:blank";
|
|
34
|
+
readonly title: "Unauthorized";
|
|
35
|
+
readonly detail: "The client must authenticate itself to get the requested response.";
|
|
36
|
+
};
|
|
37
|
+
readonly FORBIDDEN: {
|
|
38
|
+
readonly status: 403;
|
|
39
|
+
readonly type: "about:blank";
|
|
40
|
+
readonly title: "Forbidden";
|
|
41
|
+
readonly detail: "The client does not have access rights to the content.";
|
|
42
|
+
};
|
|
43
|
+
readonly NOT_FOUND: {
|
|
44
|
+
readonly status: 404;
|
|
45
|
+
readonly type: "about:blank";
|
|
46
|
+
readonly title: "Not Found";
|
|
47
|
+
readonly detail: "The server can not find the requested resource.";
|
|
48
|
+
};
|
|
49
|
+
readonly CONFLICT: {
|
|
50
|
+
readonly status: 409;
|
|
51
|
+
readonly type: "about:blank";
|
|
52
|
+
readonly title: "Conflict";
|
|
53
|
+
readonly detail: "The request conflicts with the current state of the server.";
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
export type ServerErrorKey = keyof typeof SERVER_ERRORS;
|
|
57
|
+
export type ServerErrorOptions = {
|
|
58
|
+
status?: number;
|
|
59
|
+
title?: string;
|
|
60
|
+
detail?: string;
|
|
61
|
+
type?: string;
|
|
62
|
+
data?: any;
|
|
63
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SERVER_ERRORS = void 0;
|
|
4
|
+
/** Defines standardized server error responses based on RFC 9457.
|
|
5
|
+
* Each error includes a status code, type, title, and detail message.
|
|
6
|
+
* These can be used throughout the application for consistent error handling and responses.
|
|
7
|
+
* Additional errors can be added to the SERVER_ERRORS object as needed.
|
|
8
|
+
*/
|
|
9
|
+
exports.SERVER_ERRORS = {
|
|
10
|
+
INTERNAL_SERVER_ERROR: {
|
|
11
|
+
status: 500,
|
|
12
|
+
type: "about:blank",
|
|
13
|
+
title: "Internal Server Error",
|
|
14
|
+
detail: "An unexpected error occurred on the server.",
|
|
15
|
+
},
|
|
16
|
+
SERVICE_UNAVAILABLE: {
|
|
17
|
+
status: 503,
|
|
18
|
+
type: "about:blank",
|
|
19
|
+
title: "Service Unavailable",
|
|
20
|
+
detail: "The server is currently unable to handle the request due to temporary overload or maintenance.",
|
|
21
|
+
},
|
|
22
|
+
UNKNOWN_ERROR: {
|
|
23
|
+
status: 520,
|
|
24
|
+
type: "about:blank",
|
|
25
|
+
title: "Unknown Error",
|
|
26
|
+
detail: "An unknown error occurred.",
|
|
27
|
+
},
|
|
28
|
+
BAD_REQUEST: {
|
|
29
|
+
status: 400,
|
|
30
|
+
type: "about:blank",
|
|
31
|
+
title: "Bad Request",
|
|
32
|
+
detail: "The server could not understand the request due to invalid syntax.",
|
|
33
|
+
},
|
|
34
|
+
UNAUTHORIZED: {
|
|
35
|
+
status: 401,
|
|
36
|
+
type: "about:blank",
|
|
37
|
+
title: "Unauthorized",
|
|
38
|
+
detail: "The client must authenticate itself to get the requested response.",
|
|
39
|
+
},
|
|
40
|
+
FORBIDDEN: {
|
|
41
|
+
status: 403,
|
|
42
|
+
type: "about:blank",
|
|
43
|
+
title: "Forbidden",
|
|
44
|
+
detail: "The client does not have access rights to the content.",
|
|
45
|
+
},
|
|
46
|
+
NOT_FOUND: {
|
|
47
|
+
status: 404,
|
|
48
|
+
type: "about:blank",
|
|
49
|
+
title: "Not Found",
|
|
50
|
+
detail: "The server can not find the requested resource.",
|
|
51
|
+
},
|
|
52
|
+
CONFLICT: {
|
|
53
|
+
status: 409,
|
|
54
|
+
type: "about:blank",
|
|
55
|
+
title: "Conflict",
|
|
56
|
+
detail: "The request conflicts with the current state of the server.",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./APIError"), exports);
|
|
18
|
+
__exportStar(require("./BackendErrors"), exports);
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { type ServerErrorOptions, type ServerErrorKey } from "../errors/BackendErrors";
|
|
2
|
+
import { type Static, type TObject } from "@sinclair/typebox";
|
|
3
|
+
import { type FastifyRequest } from "fastify/types/request";
|
|
4
|
+
import { type FastifyReply } from "fastify/types/reply";
|
|
5
|
+
import { type FastifyInstance } from "fastify";
|
|
6
|
+
type httpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
7
|
+
/** Type definition for the success response payload.
|
|
8
|
+
* @property {string} title - short, human-readable summary of the response
|
|
9
|
+
* @property {string} detail - detailed description of the response
|
|
10
|
+
* @property {any} data - any additional data to include in the response
|
|
11
|
+
*/
|
|
12
|
+
type SuccessResponse = {
|
|
13
|
+
title: string;
|
|
14
|
+
detail: string;
|
|
15
|
+
data: any;
|
|
16
|
+
};
|
|
17
|
+
/** Sends a standardized error response.
|
|
18
|
+
* Uses the RFC 9457 Format.
|
|
19
|
+
* @param res - Fastify reply object
|
|
20
|
+
* @param error - error code or identifier
|
|
21
|
+
* @param options - additional error details (title, detail, type, data)
|
|
22
|
+
* @returns {void}
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* return errorResponse(res, "NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function errorResponse(res: FastifyReply, error: ServerErrorKey, { title, detail, type, data, status }?: ServerErrorOptions): void;
|
|
29
|
+
/** Sends a standardized success response.
|
|
30
|
+
* @param res - Fastify reply object
|
|
31
|
+
* @param status - HTTP status code
|
|
32
|
+
* @param title - short, human-readable summary of the response
|
|
33
|
+
* @param detail - detailed description of the response
|
|
34
|
+
* @param data - any additional data to include in the response
|
|
35
|
+
* @returns {void}
|
|
36
|
+
* @example
|
|
37
|
+
* ```ts
|
|
38
|
+
* return successResponse(res, 200, "User Created", "The user was created successfully, you should recieve an email in a short while.", { id: 1, name: "Example" });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function successResponse(res: FastifyReply, status: number | undefined, { title, detail, data }: SuccessResponse): void;
|
|
42
|
+
/** Utility function to register a route with standardized request typing and optional schema validation.
|
|
43
|
+
* @param fastify - The Fastify instance to register the route on
|
|
44
|
+
* @param method - HTTP method (e.g., "get", "post")
|
|
45
|
+
* @param path - Route path (e.g., "/users/:id")
|
|
46
|
+
* @param handler - Route handler function with typed request and reply
|
|
47
|
+
* @param options - Optional route options, including schema for validation and pre-handlers
|
|
48
|
+
* @returns void
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* REGISTER_ROUTE(fastify, "post", "/users", async (req, res) => {
|
|
52
|
+
* const { name, email } = req.body;
|
|
53
|
+
* // Handle user creation logic here
|
|
54
|
+
* return successResponse(res, 201, "User created successfully", { id: newUserId });
|
|
55
|
+
* }, {
|
|
56
|
+
* schema: {
|
|
57
|
+
* body: Type.Object({
|
|
58
|
+
* name: Type.String(),
|
|
59
|
+
* email: Type.String({ format: "email" }),
|
|
60
|
+
* }),
|
|
61
|
+
* }, preHandler: async (req, res) => {
|
|
62
|
+
* // Optional pre-handler logic (e.g., authentication)
|
|
63
|
+
* }
|
|
64
|
+
* });
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export declare function REGISTER_ROUTE<BodyType extends TObject, QueryType extends TObject, ParamsType extends TObject>(fastify: FastifyInstance, method: httpMethod, path: string, handler: (req: FastifyRequest<{
|
|
68
|
+
Body: Static<BodyType>;
|
|
69
|
+
Querystring: Static<QueryType>;
|
|
70
|
+
Params: Static<ParamsType>;
|
|
71
|
+
}>, reply: any) => void, options?: {
|
|
72
|
+
schema?: {
|
|
73
|
+
body?: BodyType;
|
|
74
|
+
querystring?: QueryType;
|
|
75
|
+
params?: ParamsType;
|
|
76
|
+
};
|
|
77
|
+
preHandler?: Array<(req: FastifyRequest, reply: any) => void> | ((req: FastifyRequest, reply: any) => void);
|
|
78
|
+
}): void;
|
|
79
|
+
/** Centralized error handler for Fastify.
|
|
80
|
+
* @param error - The error object caught by Fastify
|
|
81
|
+
* @param request - The incoming request object
|
|
82
|
+
* @param reply - The Fastify reply object used to send the response
|
|
83
|
+
* @returns {Promise<void>}
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* fastify.setErrorHandler(globalErrorHandler);
|
|
87
|
+
* ```
|
|
88
|
+
*/
|
|
89
|
+
export declare function globalErrorHandler(error: any, _request: FastifyRequest, reply: FastifyReply): Promise<void>;
|
|
90
|
+
export {};
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.errorResponse = errorResponse;
|
|
7
|
+
exports.successResponse = successResponse;
|
|
8
|
+
exports.REGISTER_ROUTE = REGISTER_ROUTE;
|
|
9
|
+
exports.globalErrorHandler = globalErrorHandler;
|
|
10
|
+
const BackendErrors_1 = require("../errors/BackendErrors");
|
|
11
|
+
const colors_1 = require("@divine-lab/logger/colors");
|
|
12
|
+
const APIError_1 = require("../errors/APIError");
|
|
13
|
+
const logger_1 = __importDefault(require("@divine-lab/logger"));
|
|
14
|
+
const API_LOG = process.env.DIVINE_LAB_REQUEST_API_LOG === "true";
|
|
15
|
+
logger_1.default.raw(`${(0, colors_1.colorize)("blue", "[API]")} - API logging is ${API_LOG ? (0, colors_1.colorize)("green", "enabled") : (0, colors_1.colorize)("red", "disabled")} - ${API_LOG ? "API requests and responses will be logged." : "to enable, set API_LOG=true in environment variables"}`);
|
|
16
|
+
/** Sends a standardized error response.
|
|
17
|
+
* Uses the RFC 9457 Format.
|
|
18
|
+
* @param res - Fastify reply object
|
|
19
|
+
* @param error - error code or identifier
|
|
20
|
+
* @param options - additional error details (title, detail, type, data)
|
|
21
|
+
* @returns {void}
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* return errorResponse(res, "NOT_FOUND", { title: "Resource Not Found", detail: "The requested resource was not found." });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
function errorResponse(res, error, { title, detail, type, data, status } = {}) {
|
|
28
|
+
const errorDef = BackendErrors_1.SERVER_ERRORS[error];
|
|
29
|
+
if (API_LOG)
|
|
30
|
+
logger_1.default.raw(`${new Date().toISOString()} ${(0, colors_1.colorize)("red", "[API]")} - ${(0, colors_1.colorize)("red", (status || errorDef.status))} - ${res.request.url} : ${(0, colors_1.colorize)("gray", `${title || errorDef.title} - ${detail || errorDef.detail}`)}`);
|
|
31
|
+
res.code(status || errorDef.status).send({
|
|
32
|
+
status: status || errorDef.status,
|
|
33
|
+
instance: res.request.url,
|
|
34
|
+
title: title || errorDef.title,
|
|
35
|
+
detail: detail || errorDef.detail,
|
|
36
|
+
type: type || errorDef.type,
|
|
37
|
+
data: data || null,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/** Sends a standardized success response.
|
|
41
|
+
* @param res - Fastify reply object
|
|
42
|
+
* @param status - HTTP status code
|
|
43
|
+
* @param title - short, human-readable summary of the response
|
|
44
|
+
* @param detail - detailed description of the response
|
|
45
|
+
* @param data - any additional data to include in the response
|
|
46
|
+
* @returns {void}
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* return successResponse(res, 200, "User Created", "The user was created successfully, you should recieve an email in a short while.", { id: 1, name: "Example" });
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
function successResponse(res, status = 200, { title = "Success", detail = "Success", data = "Success" }) {
|
|
53
|
+
if (API_LOG)
|
|
54
|
+
logger_1.default.raw(`${new Date().toISOString()} ${(0, colors_1.colorize)("green", "[API]")} - ${(0, colors_1.colorize)("green", status)} - ${res.request.url} : ${(0, colors_1.colorize)("gray", title)}`);
|
|
55
|
+
res.code(status).send({
|
|
56
|
+
success: true,
|
|
57
|
+
title,
|
|
58
|
+
detail,
|
|
59
|
+
data,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Utility function to register a route with standardized request typing and optional schema validation.
|
|
63
|
+
* @param fastify - The Fastify instance to register the route on
|
|
64
|
+
* @param method - HTTP method (e.g., "get", "post")
|
|
65
|
+
* @param path - Route path (e.g., "/users/:id")
|
|
66
|
+
* @param handler - Route handler function with typed request and reply
|
|
67
|
+
* @param options - Optional route options, including schema for validation and pre-handlers
|
|
68
|
+
* @returns void
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* REGISTER_ROUTE(fastify, "post", "/users", async (req, res) => {
|
|
72
|
+
* const { name, email } = req.body;
|
|
73
|
+
* // Handle user creation logic here
|
|
74
|
+
* return successResponse(res, 201, "User created successfully", { id: newUserId });
|
|
75
|
+
* }, {
|
|
76
|
+
* schema: {
|
|
77
|
+
* body: Type.Object({
|
|
78
|
+
* name: Type.String(),
|
|
79
|
+
* email: Type.String({ format: "email" }),
|
|
80
|
+
* }),
|
|
81
|
+
* }, preHandler: async (req, res) => {
|
|
82
|
+
* // Optional pre-handler logic (e.g., authentication)
|
|
83
|
+
* }
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
function REGISTER_ROUTE(fastify, method, path, handler, options) {
|
|
88
|
+
fastify[method.toLowerCase()](path, options || {}, handler);
|
|
89
|
+
}
|
|
90
|
+
/** Centralized error handler for Fastify.
|
|
91
|
+
* @param error - The error object caught by Fastify
|
|
92
|
+
* @param request - The incoming request object
|
|
93
|
+
* @param reply - The Fastify reply object used to send the response
|
|
94
|
+
* @returns {Promise<void>}
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* fastify.setErrorHandler(globalErrorHandler);
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
async function globalErrorHandler(error, _request, reply) {
|
|
101
|
+
if (error instanceof APIError_1.APIError)
|
|
102
|
+
return errorResponse(reply, error.error, { title: error.title, detail: error.detail, type: error.type, data: error.data, status: error.status });
|
|
103
|
+
if (error.validation)
|
|
104
|
+
return errorResponse(reply, "BAD_REQUEST", { detail: error.message, data: null });
|
|
105
|
+
logger_1.default.error(`Error processing request: ${error.message}`);
|
|
106
|
+
return errorResponse(reply, "INTERNAL_SERVER_ERROR", { title: "Internal Server Error", detail: "An unexpected error occurred", type: "about:blank", data: null, status: 500 });
|
|
107
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/request.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@divine-lab/request",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Simple utility for standardizing request and response objects.",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Ishpreet Singh",
|
|
7
|
+
"email": "ishpreet7521@gmail.com",
|
|
8
|
+
"github": "https://github.com/Ishpreet-Singh-Channa",
|
|
9
|
+
"linkedin": "https://www.linkedin.com/in/ishpreet-singh-5860ab257"
|
|
10
|
+
},
|
|
11
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"request",
|
|
14
|
+
"response",
|
|
15
|
+
"request utility",
|
|
16
|
+
"response utility"
|
|
17
|
+
],
|
|
18
|
+
"main": "dist/request.js",
|
|
19
|
+
"types": "dist/request.d.ts",
|
|
20
|
+
"files": [
|
|
21
|
+
"dist"
|
|
22
|
+
],
|
|
23
|
+
"exports": {
|
|
24
|
+
"./errors": {
|
|
25
|
+
"default": "./dist/errors/index.js",
|
|
26
|
+
"types": "./dist/errors/index.d.ts"
|
|
27
|
+
},
|
|
28
|
+
"./fastify-helpers": {
|
|
29
|
+
"default": "./dist/fastify-helpers/index.js",
|
|
30
|
+
"types": "./dist/fastify-helpers/index.d.ts"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"prepublishOnly": "tsc",
|
|
35
|
+
"build": "tsc"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@sinclair/typebox": "^0.34.49",
|
|
39
|
+
"@types/node": "^25.6.2",
|
|
40
|
+
"fastify": "^5.8.5",
|
|
41
|
+
"typescript": "^6.0.3"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@divine-lab/logger": "^1.0.2"
|
|
45
|
+
}
|
|
46
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# @divine-lab/request
|
|
2
|
+
|
|
3
|
+
Simple http request formatting utility for backends.
|
|
4
|
+
Works with <a href="https://fastify.dev/">fastify</a>
|
|
5
|
+
|
|
6
|
+
### Request Formating
|
|
7
|
+
|
|
8
|
+
Provides 2 functions for request formating.
|
|
9
|
+
|
|
10
|
+
1. `successResponse`: for successs responses.
|
|
11
|
+
2. `errorResponse`: for error responses.
|
|
12
|
+
|
|
13
|
+
success response and error response both follow a defined standard.
|
|
14
|
+
These functions are defined to be used with fastify for now.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { successResponse } from "@divine-lab/request/fastify-helpers"
|
|
18
|
+
|
|
19
|
+
// Mock type
|
|
20
|
+
type SuccessResponse = {
|
|
21
|
+
success: boolean;
|
|
22
|
+
title: string;
|
|
23
|
+
detail: string;
|
|
24
|
+
data: any;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Example of a success response:
|
|
28
|
+
const userCreatedResponse: SuccessResponse = {
|
|
29
|
+
success: true,
|
|
30
|
+
title: "Account created successfully",
|
|
31
|
+
detail: "You will recieve a email confirming the account creation in a 5-10 minutes."
|
|
32
|
+
data: { id: 1, name: "user", age: 21 }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Example success response
|
|
36
|
+
successResponse(res, 200, userCreatedResponse)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { errorResponse } from "@divine-lab/request/fastify-helpers"
|
|
41
|
+
|
|
42
|
+
// Mock type
|
|
43
|
+
type ErrorResponse = {
|
|
44
|
+
status: number;
|
|
45
|
+
title: string;
|
|
46
|
+
detail: string;
|
|
47
|
+
type: string;
|
|
48
|
+
instance: string;
|
|
49
|
+
data: any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Example of Error Response
|
|
53
|
+
const userNotFoundError: ErrorResponse = {
|
|
54
|
+
status: 404,
|
|
55
|
+
title: "User Not Found",
|
|
56
|
+
detail: "The User with the user id '1' not found in our database, please contact customer support if this is a mistake.",
|
|
57
|
+
type: "http://example-errors.com/404/user-not-found",
|
|
58
|
+
instance: "/users/1"
|
|
59
|
+
data: null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// example error response
|
|
63
|
+
errorResponse(res, "UNKNOWN", userNotFoundError)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Note: RFC9457 requires error responses to contain `instance` in the response. Right now all error responses automatically attach the request url as the instance.
|
|
67
|
+
|
|
68
|
+
### Errors
|
|
69
|
+
|
|
70
|
+
Provides an APIError class which can be used to throw standardized Errors.
|
|
71
|
+
Uses the defined `ServerErrors` to provide template for errors.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import APIError from "@divine-lab/request/erros";
|
|
75
|
+
|
|
76
|
+
async function reqHandler(req, res) {
|
|
77
|
+
// ...
|
|
78
|
+
if (!user) throw new APIError("UNAUTHORIZED");
|
|
79
|
+
// ...
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Note: If a type of error is not defined, You can request for it to be added. Or you can use the `unknown` error and define the options.
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import APIError from "@divine-lab/request/erros";
|
|
87
|
+
|
|
88
|
+
async function reqHandler(req, res) {
|
|
89
|
+
// ...
|
|
90
|
+
if (unknown) throw new APIError("UNKNOWN", { status: 500, title: "Error", details: "This error was not defined", type: "", data: "" });
|
|
91
|
+
// ...
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Global Error Handler
|
|
96
|
+
|
|
97
|
+
In Fastify, you can attach a global error handler to make sure that any error can be caught in it.
|
|
98
|
+
To always send standardized error responses, It is recommended to register the global error handler in fastify.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { globalErrorHandler } from "@divine-lab/request/fastify-helpers";
|
|
102
|
+
|
|
103
|
+
// ...
|
|
104
|
+
fastify.setErrorHandler(globalErrorHandler);
|
|
105
|
+
// ...
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
It automatically formats unknown error into `Internal Server Error`.
|
|
109
|
+
Additionally if a `400 Bad request` is made, then this handler should handle it as well.
|
|
110
|
+
|
|
111
|
+
### Environment Variables
|
|
112
|
+
|
|
113
|
+
Supports API response logging via <a>`@divine-lab/logger`</a>.
|
|
114
|
+
Enable by setting ENV Variable `DIVINE_LAB_REQUEST_API_LOG` to `true`.
|
|
115
|
+
|
|
116
|
+
#### Contact:
|
|
117
|
+
|
|
118
|
+
- mail: <a href="mailto:ishpreet7521@gmail.com">ishpreet7521@gmail.com<a>
|