Skip to contentPedro Farbo
Lesson 7 / 2540 min

Error Handling

Error Handling

A robust error handling system is essential for production APIs.

Custom Error Class

typescript
// src/errors/AppError.tsexport class AppError extends Error {  public readonly statusCode: number;  public readonly isOperational: boolean;   constructor(message: string, statusCode: number = 400) {    super(message);    this.statusCode = statusCode;    this.isOperational = true;    Error.captureStackTrace(this, this.constructor);  }}

Global Error Middleware

typescript
// src/middlewares/error.middleware.tsimport { Request, Response, NextFunction } from 'express';import { AppError } from '../errors/AppError';import { ZodError } from 'zod'; export const errorHandler = (  err: Error,  req: Request,  res: Response,  next: NextFunction) => {  // Zod validation errors  if (err instanceof ZodError) {    return res.status(400).json({      status: 'error',      message: 'Validation error',      errors: err.errors,    });  }   // Our custom errors  if (err instanceof AppError) {    return res.status(err.statusCode).json({      status: 'error',      message: err.message,    });  }   // Log unexpected errors  console.error('Unexpected error:', err);   // Don't expose details in production  return res.status(500).json({    status: 'error',    message: process.env.NODE_ENV === 'production'      ? 'Internal server error'      : err.message,  });};

Using in Services

typescript
// src/services/product.service.tsimport { AppError } from '../errors/AppError'; export class ProductService {  async findById(id: string) {    const product = await prisma.product.findUnique({ where: { id } });     if (!product) {      throw new AppError('Product not found', 404);    }     return product;  }   async create(data: CreateProductDTO) {    const exists = await prisma.product.findFirst({      where: { name: data.name },    });     if (exists) {      throw new AppError('Product with this name already exists', 409);    }     return prisma.product.create({ data });  }}

Async Handler

typescript
// src/utils/async-handler.tsimport { Request, Response, NextFunction } from 'express'; export const asyncHandler = (fn: Function) => {  return (req: Request, res: Response, next: NextFunction) => {    Promise.resolve(fn(req, res, next)).catch(next);  };}; // Usagerouter.get('/:id', asyncHandler(async (req, res) => {  const product = await productService.findById(req.params.id);  res.json(product);}));

404 Handler

typescript
// After all routesapp.use((req, res) => {  res.status(404).json({    status: 'error',    message: `Route ${req.method} ${req.path} not found`,  });}); // Error handler always at the endapp.use(errorHandler);

Summary

  • ✅ Custom AppError class
  • ✅ Centralized error middleware
  • ✅ Async error handling
  • ✅ Logging and environment-aware responses

Next lesson: Introduction to Databases! 🚀

Enjoyed the content? Your contribution helps keep everything online and free!

PIX:0737160d-e98f-4a65-8392-5dba70e7ff3e