Skip to content

Instantly share code, notes, and snippets.

@navsqi
Last active November 11, 2020 08:05
Show Gist options
  • Select an option

  • Save navsqi/325220e4211b769e08f5e18e328bd914 to your computer and use it in GitHub Desktop.

Select an option

Save navsqi/325220e4211b769e08f5e18e328bd914 to your computer and use it in GitHub Desktop.
Error handling in Node.JS
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.status = `${statusCode}`.startsWith('4') ? 'fail' : 'error';
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = AppError;
const AppError = require('./../utils/appError');
const handleCastErrorDB = err => {
const message = `Invalid ${err.path}: ${err.value}.`;
return new AppError(message, 400);
};
const handleDuplicateFieldsDB = err => {
const value = err.errmsg.match(/(["'])(\\?.)*?\1/)[0];
const message = `Duplicate field value: ${value}. Please use another value!`;
return new AppError(message, 400);
};
const handleValidationErrorDB = err => {
const errors = Object.values(err.errors).map(el => el.message);
const message = `Invalid input data. ${errors.join('. ')}`;
return new AppError(message, 400);
};
const handleJWTError = () =>
new AppError('Invalid token. Please log in again!', 401);
const handleJWTExpiredError = () =>
new AppError('Your token has expired! Please log in again.', 401);
const sendErrorDev = (err, req, res) => {
// A) API
if (req.originalUrl.startsWith('/api')) {
return res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack
});
}
// B) RENDERED WEBSITE
console.error('ERROR πŸ’₯', err);
return res.status(err.statusCode).render('error', {
title: 'Something went wrong!',
msg: err.message
});
};
const sendErrorProd = (err, req, res) => {
// A) API
if (req.originalUrl.startsWith('/api')) {
// A) Operational, trusted error: send message to client
if (err.isOperational) {
return res.status(err.statusCode).json({
status: err.status,
message: err.message
});
}
// B) Programming or other unknown error: don't leak error details
// 1) Log error
console.error('ERROR πŸ’₯', err);
// 2) Send generic message
return res.status(500).json({
status: 'error',
message: 'Something went very wrong!'
});
}
// B) RENDERED WEBSITE
// A) Operational, trusted error: send message to client
if (err.isOperational) {
return res.status(err.statusCode).render('error', {
title: 'Something went wrong!',
msg: err.message
});
}
// B) Programming or other unknown error: don't leak error details
// 1) Log error
console.error('ERROR πŸ’₯', err);
// 2) Send generic message
return res.status(err.statusCode).render('error', {
title: 'Something went wrong!',
msg: 'Please try again later.'
});
};
module.exports = (err, req, res, next) => {
// console.log(err.stack);
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'development') {
sendErrorDev(err, req, res);
} else if (process.env.NODE_ENV === 'production') {
let error = { ...err };
error.message = err.message;
if (error.name === 'CastError') error = handleCastErrorDB(error);
if (error.code === 11000) error = handleDuplicateFieldsDB(error);
if (error.name === 'ValidationError')
error = handleValidationErrorDB(error);
if (error.name === 'JsonWebTokenError') error = handleJWTError();
if (error.name === 'TokenExpiredError') error = handleJWTExpiredError();
sendErrorProd(error, req, res);
}
};
const logger = require('../utils/winstonLog');
const AppError = require('./../utils/appError');
const handleCastErrorDB = (err) => {
const message = `Invalid ${err.path}: ${err.value}.`;
return new AppError(message, 400);
};
const handleDuplicateFieldsDB = (err) => {
const value = err.errors[0].message;
const message = `Duplicate field value: ${value}. Please use another value!`;
return new AppError(message, 400);
};
const handleValidationErrorDB = (err) => {
const errors = Object.values(err.errors).map((el) => el.message);
const message = `Invalid input data: ${errors.join('. ')}`;
return new AppError(message, 400);
};
const handleJWTError = () => new AppError('Invalid token. Please log in again!', 401);
const handleJWTExpiredError = () => new AppError('Your token has expired! Please log in again.', 401);
const sendErrorDev = (err, req, res) => {
// A) API
if (req.originalUrl.startsWith(process.env.ENDPOINT + '/api')) {
return res.status(err.statusCode).json({
status: err.status,
error: err,
message: err.message,
stack: err.stack,
});
}
// B) RENDERED WEBSITE
console.error('ERROR πŸ’₯', err);
return res.send(err.message);
};
const sendErrorProd = (err, req, res) => {
// A) API
if (req.originalUrl.startsWith(process.env.ENDPOINT + '/api')) {
// A) Operational, trusted error: send message to client
if (err.isOperational) {
return res.status(err.statusCode).json({
status: err.status,
message: err.message,
});
}
// B) Programming or other unknown error: don't leak error details
// 1) Log error
console.error('ERROR πŸ’₯', err);
// 2) Send generic message
return res.status(500).json({
status: 'error',
message: 'Something went very wrong!',
});
}
// B) RENDERED WEBSITE
// A) Operational, trusted error: send message to client
if (err.isOperational) {
return res.status(err.statusCode).send(err.message);
}
// B) Programming or other unknown error: don't leak error details
// 1) Log error
console.error('ERROR πŸ’₯', err);
// 2) Send generic message
return res.status(err.statusCode).render('error', {
title: 'Something went wrong!',
msg: 'Please try again later.',
});
};
module.exports = (err, req, res, next) => {
// console.log(err.stack);
err.statusCode = err.statusCode || 500;
err.status = err.status || 'error';
if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'local') {
sendErrorDev(err, req, res);
} else if (process.env.NODE_ENV === 'production') {
let error = { ...err };
error.message = err.message;
logger.error(error);
if (error.name === 'CastError') error = handleCastErrorDB(error);
if (error.name === 'SequelizeUniqueConstraintError') error = handleDuplicateFieldsDB(error);
if (error.name === 'SequelizeValidationError') error = handleValidationErrorDB(error);
if (error.name === 'JsonWebTokenError') error = handleJWTError();
if (error.name === 'TokenExpiredError') error = handleJWTExpiredError();
sendErrorProd(error, req, res);
}
};
var winston = require("winston");
const env = process.env.NODE_ENV;
const logDir = "logs";
const fs = require("fs");
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
const now = new Date();
var logger = new winston.createLogger({
transports: [
new winston.transports.File({
name: "error-file",
filename: "./logs/exceptions.log",
level: "error",
json: false,
}),
new (require("winston-daily-rotate-file"))({
filename: `${logDir}/apimodules.log`,
timestamp: now,
datePattern: "DD-MM-yyyy",
prepend: true,
json: false,
level: env === "development" ? "verbose" : "info",
}),
],
exitOnError: false,
});
module.exports = logger;
module.exports.stream = {
write: function (message, encoding) {
logger.info(message);
console.log("message=", message);
},
};
module.exports = (fn) => {
return (req, res, next) => {
fn(req, res, next).catch(next);
};
};
const fs = require('fs');
const path = require('path');
exports.deleteFileImage = (imageName, imageDir) => {
if (imageName) {
fs.unlink(
path.join(__dirname, '..', 'public', 'images', imageDir, imageName),
(err) => {
console.log(err);
if (err) return err;
}
);
}
return true;
};
const loggerFile = require('./utils/winstonLog');
// Production Error
if (process.env.NODE_ENV === 'production') {
process.on('uncaughtException', (err) => {
loggerFile.error('UNCAUGHT EXCEPTION! πŸ’₯ Shutting down...');
loggerFile.error(err.name, err.message);
console.log(err.name, err.message);
process.exit(1);
});
}
const AppError = require('./utils/appError');
const globalErrorHandler = require('./utils/globalError');
const app = express();
// Unhandled Route
app.all('*', (req, res, next) => {
next(new AppError(`Can't find route ${req.originalUrl}`, 404));
});
// Error Handler Middleware
app.use(globalErrorHandler);
// Server
const port = process.env.PORT || 3030;
const server = app.listen(port, () => console.log(`Server is listening on port ${port}`));
process.on('unhandledRejection', (err) => {
loggerFile.error(err.message);
console.log(err.message);
server.close(() => {
process.exit(1);
});
});
process.on('SIGTERM', () => {
loggerFile.info('πŸ‘‹ SIGTERM RECEIVED. Shutting down gracefully');
server.close(() => {
loggerFile.info('πŸ’₯ Process terminated!');
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment