Standardizing Express.js Error Handling with One Library

Published: (December 5, 2025 at 05:52 AM EST)
2 min read
Source: Dev.to

Source: Dev.to

Cover image for Standardizing Express.js Error Handling with One Library

If you’ve been working with Express.js for a while, you know the struggle. You start a project, and soon your controllers look like this:

// The "Bad" Way
app.post('/users', async (req, res) => {
  try {
    // ... logic
  } catch (error) {
    console.error(error);
    if (error.code === 'P2002') { // Prisma unique constraint
      return res.status(400).json({ message: 'User already exists' });
    }
    // ... 10 more if/else checks
    res.status(500).json({ message: 'Something went wrong' });
  }
});

It’s repetitive, hard to maintain, and prone to bugs. I got tired of copy‑pasting error handling logic between my projects, so I built a tool to fix it once and for all.

Meet ds-express-errors.

What is it?

ds-express-errors is a centralized, type‑safe error handling library for Node.js & Express. It replaces manual try‑catch blocks and status‑code management with a clean, declarative API.

Ready‑to‑use Presets 🛠️

Forget about remembering if “Forbidden” is 401 or 403. Just use the presets.

import { Errors } from 'ds-express-errors';

if (!user) {
  throw Errors.NotFound('User not found'); // Automatically sends 404
}

if (!user.hasAccess) {
  throw Errors.Forbidden('Access denied'); // Automatically sends 403
}

Auto‑Mapping (The Magic 🪄)

If you use Zod, Prisma, or Mongoose, the library automatically detects their native errors and converts them into readable HTTP 400 Bad Requests.

Example with Zod

Input (Invalid)

{ "email": "invalid-email" }

Output (Automatic)

{
  "status": "fail",
  "method": "GET",
  "url": "/login",
  "message": "Validation error: email: Invalid email",
  "stack": "..." // shown when NODE_ENV=development
}

No More try/catch

With the included asyncHandler, your controllers become clean again.

import { asyncHandler, Errors } from 'ds-express-errors';

const createUser = asyncHandler(async (req, res) => {
  const user = await db.create(req.body); // If this throws, it's handled automatically!
  res.json(user);
});

Graceful Shutdown 🛑

It also handles process‑level errors (uncaughtException, unhandledRejection) and allows you to close database connections gracefully before crashing.

import { initGlobalHandlers } from 'ds-express-errors';

initGlobalHandlers({
  onCrash: () => {
    db.disconnect();
    server.close();
  }
});

📦 How to use it?

Installation is standard:

npm install ds-express-errors

📚 Documentation

A full documentation site is available with advanced usage, API references, and more examples:

👉 ds-express-errors.dev

Conclusion

Building this library helped me standardize my backend projects, and I hope it helps you too. It’s fully typed (TypeScript), lightweight, and production‑ready.

Give it a try and let me know what you think! Any feedback or stars on a GitHub repository are highly appreciated. ❤️

Back to Blog

Related posts

Read more »

FULL REDUX INTERNAL

Redux Internal Flow Diagram text ┌─────────────────────────────┐ │ Your Component │ │ dispatchaction │ └──────────────┬──────────────...