diff --git a/backend/src/app.ts b/backend/src/app.ts index c043eb344..d1c13e2a5 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -30,8 +30,8 @@ import { integrationAuth as integrationAuthRouter } from './routes'; import { getLogger } from './utils/logger'; -import RequestError from './utils/requestError'; -import { InternalServerError } from './utils/errors'; +import RequestError, { LogLevel } from './utils/requestError'; +import { InternalServerError, RouteNotFoundError } from './utils/errors'; export const app = express(); @@ -53,23 +53,6 @@ if (NODE_ENV === 'production') { app.use(helmet()); } -//* Error Handling Middleware -app.use((error: RequestError|Error, req: Request, res: Response, next: NextFunction)=>{ - if(res.headersSent) return next(); - - if(!(error instanceof RequestError)){ - error = InternalServerError({context: {exception: error.message}, stack: error.stack}) - getLogger('backend-main').log((error).levelName.toLowerCase(), (error).message) - } - //* Sentry Error Capture - if(req.user !== undefined || req.user !== null) - Sentry.setUser({ email: req.user.email }) - Sentry.captureException(error) - - res.status((error).statusCode).json((error).format(req)) - next() -}) - // routers app.use('/api/v1/signup', signupRouter); @@ -90,6 +73,37 @@ app.use('/api/v1/stripe', stripeRouter); app.use('/api/v1/integration', integrationRouter); app.use('/api/v1/integration-auth', integrationAuthRouter); + +//* Handle unrouted requests and respond with proper error message as well as status code +app.use((req, res, next)=>{ + if(res.headersSent) return next(); + next(RouteNotFoundError({message: `The requested source '(${req.method})${req.url}' was not found`})) +}) + +//* Error Handling Middleware (must be after all routing logic) +app.use((error: RequestError|Error, req: Request, res: Response, next: NextFunction)=>{ + if(res.headersSent) return next(); + //TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError + if(!(error instanceof RequestError)){ + error = InternalServerError({context: {exception: error.message}, stack: error.stack}) + getLogger('backend-main').log((error).levelName.toLowerCase(), (error).message) + } + + //* Set Sentry user identification if req.user is populated + if(req.user !== undefined && req.user !== null){ + Sentry.setUser({ email: req.user.email }) + } + //* Only sent error to Sentry if LogLevel is one of the following level 'ERROR', 'EMERGENCY' or 'CRITICAL' + //* with this we will eliminate false-positive errors like 'BadRequestError', 'UnauthorizedRequestError' and so on + if([LogLevel.ERROR, LogLevel.EMERGENCY, LogLevel.CRITICAL].includes((error).level)){ + Sentry.captureException(error) + } + + res.status((error).statusCode).json((error).format(req)) + next() +}) + + export const server = app.listen(PORT, () => { getLogger("backend-main").info(`Server started listening at port ${PORT}`) });