API Design with Express: The Controller Pattern
In a MERN app, Express acts as the glue. To keep it maintainable, we separate logic into Routes, Controllers, and Services.
The MVC Pattern (Simplified)
- Routes: Define the endpoints (
/api/users). - Controllers: Handle the logic (getting data from the body, calling the service/model).
- Models: The Mongoose definitions.
javascript code// controllers/userController.js exports.getAllUsers = catchAsync(async (req, res, next) => { const users = await User.find(); res.status(200).json({ status: 'success', results: users.length, data: { users } }); });
Global Error Handling
Never use try/catch in every controller. Use a wrapper and a global error handler for clean, professional code.
javascript code// utils/catchAsync.js module.exports = fn => { return (req, res, next) => { fn(req, res, next).catch(next); }; };
Input Validation
The industry standard for schema validation in Express is Joi or Zod.
javascript codeconst schema = z.object({ email: z.string().email(), password: z.string().min(8), }); // Validate inside a middleware const validate = (req, res, next) => { schema.parse(req.body); next(); };
Statelessness
Your backend should not store sessions in memory. Use JWT (JSON Web Tokens) so that any instance of your server can handle any request.