Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
330dc8d
chore: create stub package for error handler
anveshdzangolab Jul 29, 2025
6f1971a
feat: add types and remove unused files
anveshdzangolab Jul 29, 2025
4987b23
feat: add fastify sensible and register it with error handler
anveshdzangolab Jul 29, 2025
46eb14c
feat: export plugin with necessary types
anveshdzangolab Jul 29, 2025
d27d9cc
docs: update README
anveshdzangolab Jul 29, 2025
1db5e14
feat: add stackTrace config property
anveshdzangolab Jul 29, 2025
aa7daf9
feat: check stack trace enabled to return stack with error
anveshdzangolab Jul 29, 2025
b945f99
chore: remove unused comments
anveshdzangolab Jul 30, 2025
47af02e
docs: updated README
anveshdzangolab Jul 30, 2025
11bf2b6
Merge branch 'main' of github.com:prefabs-tech/fastify into feat/erro…
rameshlohala Aug 1, 2025
3d2fcc5
chore: update version to 0.88.2 and add error handler dependency
rameshlohala Aug 1, 2025
39c0db8
refactor: refactor error handler
rameshlohala Aug 11, 2025
373eb8e
chore: update dependencies
rameshlohala Aug 11, 2025
56d91ed
refactor: add setErrorHandler config to toggle setting error supertok…
rameshlohala Aug 11, 2025
f86b5cd
feat: add error handler plugin and add support for pre error handler
rameshlohala Aug 12, 2025
d5ad4bf
fix: pass error to error handler if pre error handler throws an error
rameshlohala Aug 12, 2025
ead6328
Merge branch 'main' of github.com:prefabs-tech/fastify into feat/erro…
rameshlohala Aug 12, 2025
a5f24ea
chore: remove unused dependencies
rameshlohala Aug 12, 2025
402288b
docs: update readme file
rameshlohala Aug 12, 2025
49d5430
chore: update dependencies
rameshlohala Aug 12, 2025
1c16a04
chore: rename package directory
rameshlohala Aug 12, 2025
e2a8cf2
chore: update dependencies
rameshlohala Aug 12, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/error-handler/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.eslintrc.cjs
coverage
dist
node_modules
4 changes: 4 additions & 0 deletions packages/error-handler/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
root: true,
extends: ["@prefabs.tech/eslint-config/fastify"],
};
4 changes: 4 additions & 0 deletions packages/error-handler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**/*.log*
/coverage
/dist
/node_modules
76 changes: 76 additions & 0 deletions packages/error-handler/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# @prefabs.tech/fastify-error-handler

A [Fastify](https://github.com/fastify/fastify) plugin that provides an easy integration of error handler in fastify API.

## Requirements

* [@prefabs.tech/fastify-config](../config/)
* [@fastify/sensible](https://github.com/fastify/fastify-sensible)

## Installation

Install with npm:

```bash
npm install @prefabs.tech/fastify-error-handler
```

Install with pnpm:

```bash
pnpm add --filter "@scope/project @prefabs.tech/fastify-error-handler
```

## Usage

### Register Plugin

Register @prefabs.tech/fastify-error-handler package with your Fastify instance:

Note: Register the errorHandler plugin as early as possible (Before all your routes and plugin registration).

```typescript
import errorHandlerPlugin from "@prefabs.tech/fastify-error-handler";
import Fastify from "fastify";

const start = async () => {
// Create fastify instance
const fastify = Fastify();

// Register fastify-error-handler plugin
await fastify.register(errorHandlerPlugin, {});

await fastify.listen({
port: config.port,
host: "0.0.0.0",
});
};

start();
```
### Options

#### stackTrace

When enabled, the error handler will include the error’s stack trace in the HTTP response body.

By default, it is set to false.

```ts
stackTrace?: boolean; // Default: false
```

#### preErrorHandler

preErrorHandler is an optional error handler that runs before the default error handler logic.
It allows you to intercept specific errors, handle them yourself, and prevent the default handler from running.

This is especially useful when you need to integrate with other libraries that have their own error formats — for example, handling SuperTokens errors before your API’s standard error response.

```ts
preErrorHandler?: (
error: FastifyError,
request: FastifyRequest,
reply: FastifyReply,
) => void | Promise<void>;
```
67 changes: 67 additions & 0 deletions packages/error-handler/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "@prefabs.tech/fastify-error-handler",
"version": "0.88.2",
"description": "Fastify error-handler plugin",
"homepage": "https://github.com/prefabs-tech/fastify/tree/main/packages/error-handler#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/prefabs-tech/fastify.git",
"directory": "packages/error-handler"
},
"license": "MIT",
"type": "module",
"exports": {
".": {
"import": "./dist/prefabs-tech-fastify-error-handler.js",
"require": "./dist/prefabs-tech-fastify-error-handler.umd.cjs"
}
},
"main": "./dist/prefabs-tech-fastify-error-handler.umd.cjs",
"module": "./dist/prefabs-tech-fastify-error-handler.js",
"types": "./dist/types/index.d.ts",
"files": [
"dist"
],
"scripts": {
"build": "vite build && tsc --emitDeclarationOnly && mv dist/src dist/types",
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"sort-package": "npx sort-package-json",
"typecheck": "tsc --noEmit -p tsconfig.json --composite false"
},
"dependencies": {
"@fastify/sensible": "6.0.3",
"stack-trace":"1.0.0-pre2"
},
"devDependencies": {
"@prefabs.tech/eslint-config": "0.2.0",
"@prefabs.tech/tsconfig": "0.2.0",
"@types/node": "20.19.9",
"@types/stack-trace": "0.0.33",
"@typescript-eslint/eslint-plugin": "8.38.0",
"@typescript-eslint/parser": "8.38.0",
"@vitest/coverage-istanbul": "3.2.4",
"eslint": "8.57.1",
"eslint-config-prettier": "9.1.2",
"eslint-import-resolver-alias": "1.1.2",
"eslint-import-resolver-typescript": "3.10.1",
"eslint-plugin-import": "2.32.0",
"eslint-plugin-n": "14.0.0",
"eslint-plugin-prettier": "5.5.3",
"eslint-plugin-promise": "7.2.1",
"eslint-plugin-unicorn": "56.0.1",
"fastify": "5.4.0",
"fastify-plugin": "5.0.1",
"prettier": "3.6.2",
"typescript": "5.8.3",
"vite": "6.3.5",
"vitest": "3.2.4"
},
"peerDependencies": {
"fastify": ">=5.2.1",
"fastify-plugin": ">=5.0.1"
},
"engines": {
"node": ">=20"
}
}
82 changes: 82 additions & 0 deletions packages/error-handler/src/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { STATUS_CODES } from "node:http";

import { HttpError } from "@fastify/sensible";
import { FastifyReply, FastifyRequest } from "fastify";
import { parse } from "stack-trace";

import { CustomError } from "./utils/error";

import type { ErrorResponse } from "./types";

const getHttpStatusText = (statusCode: number): string =>
STATUS_CODES[statusCode] ?? "Internal Server Error";

export const errorHandler = (
error: Error,
request: FastifyRequest,
reply: FastifyReply,
) => {
const { log: logger } = request;

const isStackTraceEnabled = request.server.stackTrace || false;

const isHttpError = error instanceof HttpError;

if (isHttpError) {
const statusCode = error.statusCode || 500;

if (statusCode >= 500) {
logger.error(error);
} else if (statusCode >= 400) {
logger.info(error);
} else {
logger.error(error);
}

const response: ErrorResponse = {
code: error.code,
error: error.error || getHttpStatusText(statusCode),
message: error.message,
name: error.name,
statusCode,
};

if (isStackTraceEnabled && error.stack) {
response.stack = parse(error);
}

void reply.code(statusCode).send(response);

return;
}

let message = "Server error, please contact support";
let code = "INTERNAL_SERVER_ERROR";

if (error instanceof CustomError) {
code = error.code || code;
message = "Server has an error that is not handled, please contact support";
}

if (isStackTraceEnabled && error.stack) {
const response: ErrorResponse = {
code: code,
message: error.message,
name: error.name,
statusCode: 500,
};

response.stack = parse(error);

void reply.code(500).send(response);

return;
}

// remove stack and message from error
delete error.stack;
error.message = message;

// let fastify handle the error
throw error;
};
18 changes: 18 additions & 0 deletions packages/error-handler/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { HttpErrors } from "@fastify/sensible";

declare module "fastify" {
interface FastifyInstance {
httpErrors: HttpErrors;
stackTrace: boolean;
}
}

export { default } from "./plugin";

export { errorHandler } from "./errorHandler";

export { CustomError } from "./utils/error";

export type { HttpErrors } from "@fastify/sensible";

export type * from "./types";
36 changes: 36 additions & 0 deletions packages/error-handler/src/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import fastifySensible from "@fastify/sensible";
import FastifyPlugin from "fastify-plugin";

import { errorHandler } from "./errorHandler";

import type { ErrorHandlerOptions } from "./types";
import type { FastifyInstance } from "fastify";

const plugin = async (
fastify: FastifyInstance,
options: ErrorHandlerOptions,
) => {
fastify.log.info("Registering fastify-error-handler plugin");

fastify.decorate("stackTrace", options.stackTrace || false);

await fastify.register(fastifySensible);

fastify.setErrorHandler(async (error, request, reply) => {
if (options.preErrorHandler) {
try {
await options.preErrorHandler(error, request, reply);
} catch {
// If preErrorHandler throws an error, we can ignore it and continue
}

if (reply.sent) {
return;
}
}

return errorHandler(error, request, reply);
});
};

export default FastifyPlugin(plugin);
27 changes: 27 additions & 0 deletions packages/error-handler/src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { FastifyError, FastifyRequest, FastifyReply } from "fastify";

import type { StackFrame } from "stack-trace";

type ErrorHandler = (
error: FastifyError,
request: FastifyRequest,
reply: FastifyReply,
) => void | Promise<void>;

interface ErrorHandlerOptions {
preErrorHandler?: ErrorHandler;
stackTrace?: boolean;
}

type ErrorResponse = {
error?: string;
code?: string;
message: string;
name: string;
stack?: StackFrame[];
statusCode: number;
};

export type { ErrorHandler, ErrorHandlerOptions, ErrorResponse };

export { type StackFrame } from "stack-trace";
13 changes: 13 additions & 0 deletions packages/error-handler/src/utils/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export class CustomError extends Error {
public code?: string;

constructor(message: string, code?: string) {
super(message);

this.code = code;
this.name = this.constructor.name; // sets name to "CustomError" so that it works in logs

// (error instanceof CustomError) and (error instanceof Error) both works because of this
Object.setPrototypeOf(this, new.target.prototype);
}
}
9 changes: 9 additions & 0 deletions packages/error-handler/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@prefabs.tech/tsconfig/fastify.json",
"compilerOptions": {
"outDir": "./dist",
},
"include": [
"src/**/*.ts"
]
}
Loading