🥅 General error handling

Signed-off-by: Pau Costa <mico@micodev.es>
CR
Pau Costa Ferrer 2024-02-01 18:30:48 +01:00
parent 4582f78afd
commit 4ee5d10c39
6 changed files with 181 additions and 8 deletions

View File

@ -15,6 +15,7 @@
"typeorm": "0.3.20" "typeorm": "0.3.20"
}, },
"devDependencies": { "devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^16.11.10", "@types/node": "^16.11.10",
"ts-node": "10.9.1", "ts-node": "10.9.1",
"typescript": "4.5.2" "typescript": "4.5.2"
@ -111,12 +112,100 @@
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"devOptional": true "devOptional": true
}, },
"node_modules/@types/body-parser": {
"version": "1.19.5",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
"dev": true,
"dependencies": {
"@types/connect": "*",
"@types/node": "*"
}
},
"node_modules/@types/connect": {
"version": "3.4.38",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/express": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz",
"integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==",
"dev": true,
"dependencies": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "^4.17.33",
"@types/qs": "*",
"@types/serve-static": "*"
}
},
"node_modules/@types/express-serve-static-core": {
"version": "4.17.42",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz",
"integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==",
"dev": true,
"dependencies": {
"@types/node": "*",
"@types/qs": "*",
"@types/range-parser": "*",
"@types/send": "*"
}
},
"node_modules/@types/http-errors": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
"dev": true
},
"node_modules/@types/mime": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
"dev": true
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "16.18.77", "version": "16.18.77",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.77.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.77.tgz",
"integrity": "sha512-zwqAbRkHjGlxH9PBv8i9dmeaDpBRgfQDSFuREMF2Z+WUi8uc13gfRquMV/8LxBqwm+7jBz+doTVkEEA1CIWOnQ==", "integrity": "sha512-zwqAbRkHjGlxH9PBv8i9dmeaDpBRgfQDSFuREMF2Z+WUi8uc13gfRquMV/8LxBqwm+7jBz+doTVkEEA1CIWOnQ==",
"devOptional": true "devOptional": true
}, },
"node_modules/@types/qs": {
"version": "6.9.11",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz",
"integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==",
"dev": true
},
"node_modules/@types/range-parser": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
"dev": true
},
"node_modules/@types/send": {
"version": "0.17.4",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
"dev": true,
"dependencies": {
"@types/mime": "^1",
"@types/node": "*"
}
},
"node_modules/@types/serve-static": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz",
"integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==",
"dev": true,
"dependencies": {
"@types/http-errors": "*",
"@types/mime": "*",
"@types/node": "*"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",

View File

@ -4,16 +4,17 @@
"description": "Awesome project developed with TypeORM.", "description": "Awesome project developed with TypeORM.",
"type": "commonjs", "type": "commonjs",
"devDependencies": { "devDependencies": {
"ts-node": "10.9.1", "@types/express": "^4.17.21",
"@types/node": "^16.11.10", "@types/node": "^16.11.10",
"ts-node": "10.9.1",
"typescript": "4.5.2" "typescript": "4.5.2"
}, },
"dependencies": { "dependencies": {
"typeorm": "0.3.20", "body-parser": "^1.19.1",
"reflect-metadata": "^0.1.13",
"mysql": "^2.14.1",
"express": "^4.17.2", "express": "^4.17.2",
"body-parser": "^1.19.1" "mysql": "^2.14.1",
"reflect-metadata": "^0.1.13",
"typeorm": "0.3.20"
}, },
"scripts": { "scripts": {
"start": "ts-node src/index.ts", "start": "ts-node src/index.ts",

View File

@ -0,0 +1,52 @@
import {NextFunction, Request, Response} from "express";
import {AppError} from "../../util/AppError";
function sendDevError(res: Response, err: AppError){
// During development, we want full error information
return res.status(err.statusCode).json({
status: err.status,
message: err.message,
stack: err.stack,
err
})
}
function sendProdError(res: Response, err: AppError){
if (err.isOperational){
res.status(err.statusCode).json({
status: err.status,
message: err.message
})
}
// Non-operational errors may leak information about vulnerabilities
console.log('ERROR', err);
res.status(500).json({
status: "Error",
message: "Something went wrong on our side!"
})
}
export async function errorHandler(
err: AppError,
_req: Request,
res: Response,
_next: NextFunction
){
// Errors thrown by libraries
if (err.name === 'JsonWebTokenError'){
err = new AppError('Invalid token. Please log in again', 401);
}
if (err.name === 'TokenExpiredError'){
err = new AppError('Your token has expired. Please log in again', 401);
}
// If we are in dev use sendDevError
if (process.env.NODE_ENV === 'development'){
sendDevError(res, err);
return
}
sendProdError(res, err)
}

View File

@ -4,6 +4,7 @@ import { Request, Response } from "express"
import { AppDataSource } from "./data-source" import { AppDataSource } from "./data-source"
import { Routes } from "./routes" import { Routes } from "./routes"
import { User } from "./entity/User" import { User } from "./entity/User"
import {errorHandler} from "./controller/errorController";
AppDataSource.initialize().then(async () => { AppDataSource.initialize().then(async () => {
@ -25,8 +26,7 @@ AppDataSource.initialize().then(async () => {
}) })
// setup express app here // setup express app here
// ... app.use(errorHandler)
// start express server // start express server
app.listen(3000) app.listen(3000)

23
server/util/AppError.ts Normal file
View File

@ -0,0 +1,23 @@
enum Status {
fail = "Failure",
error = "Error",
success = "Success"
}
export class AppError extends Error {
statusCode: number;
status: Status;
// Errors caused by the user are operational
isOperational: boolean;
constructor(message: string, statusCode: number ){
super(message);
this.statusCode = statusCode;
this.isOperational = true;
this.status = `${statusCode}`.startsWith("4") ? Status.fail : Status.error;
Error.captureStackTrace(this, this.constructor);
}
}

View File

@ -0,0 +1,8 @@
import {NextFunction, Request, Response} from "express";
export function catchAsync
(fn: (req: Request, res: Response, next: NextFunction) => Promise<Response | NextFunction>){
return async (req: Request, res: Response, next: NextFunction) => {
await fn(req, res, next).catch(next);
};
}