Skip to main content

Error Handling

When it comes to error handling in Node.js TypeScript APIs, there are several best practices and approaches you can follow. ExpressoTS provides a simple and easy way to handle errors.

  • We use HTTP status codes appropriately: HTTP status codes are used to indicate the status of a response. It is important to use them appropriately in your API to indicate the success or failure of an operation.

  • We use a consistent error format: Define a consistent error format across your API so that consumers can easily understand and handle errors.

  • We handle errors in middleware: Middleware functions are a great way to handle errors in a centralized location.

  • We use try-catch blocks: Use try-catch blocks to handle synchronous errors in your code. If an error occurs in the try block, the catch block can handle it. Be sure to throw the error so that it can be handled by our error handling middleware.

  • We use async/await error handling: When using async/await, you can use try-catch blocks to handle synchronous errors in your code. However, you also need to handle any asynchronous errors that may occur.

  • We log errors: Logging errors is important for debugging and monitoring.

Our approach

We developed a standardized error reporting class called Report that provides a centralized location for throwing and handling errors, which can simplify error handling throughout the application.

By defining a standard error response format, it helps to ensure consistency in error messages that are returned to clients.

This approach is best used in applications with a large codebase or complex business logic, where errors may occur frequently and need to be handled consistently across different parts of the application.

Report error

Report class is a utility class to manage and throw application-specific errors.

class Report {
/**
* The Error method is responsible for generating a standardized error object,
* logging the error, and then throwing it for further handling.
* The error thrown is of the custom type AppError, which extends the built-in Error class.
*
* @param error - An instance of Error or a string that describes the error.
* @param statusCode - The HTTP status code associated with the error (default is 500).
* @param service - The service name associated with the error. If not specified,
* it defaults to the name of the calling function.
*
* @throws An object of the custom type AppError, which includes details about the error.
*/
public Error(
error: Error | string,
statusCode?: number,
service?: string,
): AppError {
}

Once you report a known error through the Report.Error() method, the error will be handled by the defaultErrorHandler() middleware and will be returned to the client in the json parsed format.

Middleware

This middleware function is used to handle errors that occur during request processing.

/**
* errorHandler is a custom Express error-handling middleware function.
* It logs the error, sets the status code, and sends a JSON response containing the status code and error message.
* @param error - An instance of IAppError containing error details.
* @param req - The Express request object.
* @param res - The Express response object.
* @param next - The Express next function for passing control to the next middleware function.
*/
function defaultErrorHandler(error: Error, req: Request, res: Response, next: NextFunction): void {
if (error instanceof AppError) {
res.status(error.statusCode).json({ statusCode: error.statusCode, error: error.message });
} else {
res.status(StatusCode.InternalServerError).json({
statusCode: StatusCode.InternalServerError,
error: "An unexpected error occurred.",
});
}
}

export default defaultErrorHandler;
info

function defaultErrorHandler() is a custom Express error-handling middleware function. It logs the error, sets the status code, and sends a JSON response containing the status code and error message.

Example of use

class FooClass {
constructor(private report: Report) {}

execute() {
try {
// do something
} catch (error: any) {
this.report.Error(error, StatusCode.BadRequest, "your-service");
}
}
}

Use case example:

@provide(CreateUserUseCase)
class CreateUserUseCase {
constructor(private userRepository: UserRepository, private report: Report) {}

execute(data: ICreateUserRequestDTO): ICreateUserResponseDTO | null {
try {
const { name, email } = data;

const userAlreadyExists = await this.userRepository.findByEmail(email);

if (userAlreadyExists) {
this.report.Error(
"User already exists",
StatusCode.BadRequest,
"create-user-usecase"
);
}

const user: User | null = this.userRepository.create(new User(name, email));

let response: ICreateUserResponseDTO;

if (user !== null) {
response = {
id: user.Id,
name: user.name,
email: user.email,
status: "success",
};
return response;
}

return null;
} catch (error: any) {
throw error;
}
}
}

Error components description

ObjectDescription
Report.ErrorMethod to report known errors.
AppErrorApp Error class that defines error object format.
StatusCodeHttp responses code and message.
Error MessageError message detail that the developer wants to log.
Error ServiceTo be used in the log system to indicate where the error was generated.

Support the project

ExpressoTS is an MIT-licensed open source project. It's an independent project with ongoing development made possible thanks to your support. If you'd like to help, please consider: