Site icon FSIBLOG

How I Fix Error Handling Not Working in Nuxt.js serverMiddleware

How I Fix Error Handling Not Working in Nuxt.js serverMiddleware

How I Fix Error Handling Not Working in Nuxt.js serverMiddleware

Recently, I stumbled into a head-scratcher while building APIs inside Nuxt.js v2 using Express in the serverMiddleware/ directory. I expected Express’ standard next(error) behavior to just work. Instead? Nuxt completely ignored my custom error handlers and threw a generic 500 Internal Server Error with a stack trace. After digging into it for far too long (and nearly blaming myself), I figured out what was happening and more importantly, how to fix it cleanly.

The First Attempt

Like most Express developers, I started with a simple custom error setup:

// serverMiddleware/errors.js
class HTTPError extends Error {
  constructor(status) {
    super();
    this.status = status;
  }
}

export class BadRequestError extends HTTPError {
  constructor() {
    super(400);
  }
}
// serverMiddleware/index.js
import express from "express";
import { BadRequestError } from "./errors.js";

const app = express();

app.get("/", async (req, res, next) => {
  try {
    if (typeof req.query.foo !== "string") throw new BadRequestError();
    res.sendStatus(200);
  } catch (error) {
    next(error); // Expecting Express to catch this
  }
});

// Expecting Express to handle errors — but Nuxt NEVER reaches here
app.use((err, req, res, next) => {
  console.error("Custom handler:", err);
  res.status(err.status || 500).send("Something broke!");
});

export default app;

In a standalone Express app: Works perfectly.
Inside Nuxt serverMiddleware: Completely ignored I always get a 500 + stack trace.

What’s Actually Going On

Nuxt intercepts errors before Express can handle them.

Even if you call next(err), Express’ built-in or custom error handlers never get a chance to run. Nuxt has its own internal error layer, and it bypasses Express’ pipeline entirely.

So the takeaway is:

If you’re using Express inside Nuxt serverMiddleware, do NOT rely on Express’ error handling.

The Correct Fix Handle Errors Manually

Instead of depending on Express’ global error middleware, I changed my approach:

Wrap each route with a safe async handler
Return structured JSON errors manually

Here’s the improved version:

serverMiddleware/errors.js

class HTTPError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}

export class BadRequestError extends HTTPError {
  constructor(message = "Bad Request") {
    super(400, message);
  }
}

export class NotFoundError extends HTTPError {
  constructor(message = "Not Found") {
    super(404, message);
  }
}

serverMiddleware/index.js

import express from "express";
import { BadRequestError } from "./errors.js";

const app = express();
app.use(express.json());

// Helper to safely wrap async handlers
const withErrorHandling = (handler) => async (req, res) => {
  try {
    await handler(req, res);
  } catch (err) {
    return res.status(err.status || 500).json({
      success: false,
      error: err.message || "Internal Server Error",
    });
  }
};

// GET with validation
app.get(
  "/",
  withErrorHandling(async (req, res) => {
    if (typeof req.query.foo !== "string") {
      throw new BadRequestError("Query parameter 'foo' is required");
    }
    res.json({ success: true, foo: req.query.foo });
  })
);

// POST example
app.post(
  "/users",
  withErrorHandling(async (req, res) => {
    const { email } = req.body;
    if (!email) throw new BadRequestError("Email is required");
    res.json({ success: true, message: "User created", email });
  })
);

export default app;

Optional Bonus: Simple Request Logger

app.use((req, _, next) => {
  console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
  next();
});

Final Thought

This issue wasn’t about Express at all it was about Nuxt quietly hijacking the error flow. Once I stopped trying to force Express-style error handling and embraced manual responses, everything clicked. If you’re using Express inside Nuxt serverMiddleware, treat each route as self contained handle errors inline, return consistent JSON, and don’t rely on next(err).

Exit mobile version