diff --git a/.changeset/great-moose-rhyme.md b/.changeset/great-moose-rhyme.md new file mode 100644 index 0000000000..4af0e97ff6 --- /dev/null +++ b/.changeset/great-moose-rhyme.md @@ -0,0 +1,22 @@ +--- +"@vue-storefront/middleware": minor +--- + +**[ADDED]** `defaultErrorHandler` is now exported from the package. Example usage: + +```ts +import type { Integration } from "@vue-storefront/middleware"; +import type { MiddlewareConfig } from "@vsf-enterprise/sapcc-api"; +import { defaultErrorHandler } from "@vue-storefront/middleware"; + +export const config = { + integrations: { + commerce: { + errorHandler: (error, req, res) => { + // Perform custom actions before delegating to the default error handler + defaultErrorHandler(error, req, res); + } + } satisfies Integration, + }, +}; +``` \ No newline at end of file diff --git a/docs/content/3.middleware/2.guides/8.custom-error-handler.md b/docs/content/3.middleware/2.guides/8.custom-error-handler.md index a7f6c87ecb..a79156556b 100644 --- a/docs/content/3.middleware/2.guides/8.custom-error-handler.md +++ b/docs/content/3.middleware/2.guides/8.custom-error-handler.md @@ -3,14 +3,17 @@ The Server Middleware comes with a default error handler that logs errors to the console and sends a generic error response to the client. You can customize this behavior by providing your own error handler function in the `middleware.config.ts` file: ```typescript +import type { Integration } from "@vue-storefront/middleware"; +import type { MiddlewareConfig } from "@vsf-enterprise/sapcc-api"; + export default { integrations: { sapcc: { location: '@vsf-enterprise/sapcc-api/server', errorHandler: ( - error: unknown, /** Error thrown by API method */ - req: Request, /** Express Request object */ - res: Response /** Express Response object */ + error + req, + res, ) => { res.status(404); res.send('Custom not-found error handler'); @@ -18,7 +21,7 @@ export default { configuration: { ... }, ... // remaining configuration - } + } satisfies Integration } } ``` diff --git a/packages/middleware/src/errors/defaultErrorHandler.ts b/packages/middleware/src/errors/defaultErrorHandler.ts index 8f9cdba261..006d4a7168 100644 --- a/packages/middleware/src/errors/defaultErrorHandler.ts +++ b/packages/middleware/src/errors/defaultErrorHandler.ts @@ -1,32 +1,21 @@ -import type { Request } from "express"; import { getAgnosticStatusCode } from "../helpers"; -import type { AlokaiResponse } from "../types"; - -type ClientSideError = { - message?: string; -}; +import type { Integration } from "../types"; /** * Default error handler for the middleware - * - * @param error - * @param req - * @param res */ -export const defaultErrorHandler = ( - error: ClientSideError, - req: Request, - res: AlokaiResponse +export const defaultErrorHandler: Integration["errorHandler"] = ( + error, + _req, + res ) => { const status = getAgnosticStatusCode(error); res.status(status); if (status < 500) { - const errMsg = - error?.message ?? `Request failed with status code ${status}`; /** * For all 4xx error codes or client error codes we wanted to send the error message */ - res.send({ message: errMsg }); + res.send({ message: getClientSideErrorMessage(error, status) }); } else { /** * For all other error codes we wanted to send a generic error message @@ -36,3 +25,15 @@ export const defaultErrorHandler = ( ); } }; + +const getClientSideErrorMessage = (error: unknown, status: number) => { + if ( + typeof error === "object" && + error !== null && + "message" in error && + typeof error.message === "string" + ) { + return error.message; + } + return `Request failed with status code ${status}`; +}; diff --git a/packages/middleware/src/errors/index.ts b/packages/middleware/src/errors/index.ts new file mode 100644 index 0000000000..1c4d93604f --- /dev/null +++ b/packages/middleware/src/errors/index.ts @@ -0,0 +1 @@ +export * from "./defaultErrorHandler"; diff --git a/packages/middleware/src/index.ts b/packages/middleware/src/index.ts index a8ec52c4b5..656706d151 100644 --- a/packages/middleware/src/index.ts +++ b/packages/middleware/src/index.ts @@ -3,3 +3,4 @@ export * from "./createServer"; export * from "./apiClientFactory"; export * from "./terminus"; export { getLogger } from "./logger"; +export { defaultErrorHandler } from "./errors"; diff --git a/packages/middleware/src/types/common.ts b/packages/middleware/src/types/common.ts index 6835828058..0dba3ec1fc 100644 --- a/packages/middleware/src/types/common.ts +++ b/packages/middleware/src/types/common.ts @@ -126,6 +126,42 @@ export interface Integration< ) => ApiClientExtension[]; customQueries?: Record; initConfig?: TObject; + /** + * Custom error handler for middleware. + * + * This function is invoked whenever an error occurs during middleware execution. + * Alokai provides a default error handler, which will be used if this property is not set. + * + * @param {unknown} error - The error object or value that triggered the handler. + * @param {AlokaiRequest} req - The HTTP request object associated with the error. + * @param {AlokaiResponse} res - The HTTP response object for sending a response. + * + * @example + * ```ts + * { + * errorHandler: (error, req, res) => { + * if (typeof error === "object" && error !== null && "message" in error) { + * res.status(500).send({ message: (error as any).message }); + * } else { + * res.status(500).send({ message: "An unknown error occurred" }); + * } + * } + * } + * ``` + * + * @example + * Using the default error handler with custom behavior + * ```ts + * import { defaultErrorHandler } from "@vue-storefront/middleware"; + * + * { + * errorHandler: (error, req, res) => { + * // Perform custom actions before delegating to the default error handler + * defaultErrorHandler(error, req, res); + * } + * }; + * ``` + */ errorHandler?: ( error: unknown, req: AlokaiRequest,