Skip to content

Commit

Permalink
feat(api, safe_error_serialize): add Sentry's error handler
Browse files Browse the repository at this point in the history
  • Loading branch information
toverux committed Jun 26, 2017
1 parent 07341ce commit 271d808
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 7 deletions.
9 changes: 7 additions & 2 deletions src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import * as express from 'express';
import * as raven from 'raven';
import conversionsApi from './conversions_api';
import { errorHandler } from './middlewares';
import { fatalErrorsHandler, recoverableErrorsHandler } from './middlewares';

const router: express.Router = express.Router();

//=> API endpoint roots
router.use('/conversions', conversionsApi);

router.use(errorHandler());
//=> Install error handlers
router.use(recoverableErrorsHandler());
router.use(raven.errorHandler());
router.use(fatalErrorsHandler());

export default router;
21 changes: 17 additions & 4 deletions src/api/middlewares.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { wrapAsync } from '../express_utils';
import { safeErrorSerialize } from '../safe_error_serialize';
import { isKeyValid } from './api_keys_cache';

interface ISentryResponse extends Response {
sentry?: string;
}

export function hasValidApiKey(): Handler {
return wrapAsync(async (req: Request, res: Response, next: NextFunction): Promise<void> => {
const authHeader = req.header('Authorization');
Expand All @@ -22,7 +26,7 @@ export function hasValidApiKey(): Handler {
});
}

export function errorHandler(): ErrorRequestHandler {
export function recoverableErrorsHandler(): ErrorRequestHandler {
return (err: any, req: Request, res: Response, next: NextFunction): void => {
//=> Headers already sent, let Express handle the thing
if (res.headersSent) {
Expand All @@ -43,10 +47,19 @@ export function errorHandler(): ErrorRequestHandler {
});
}

//=> In all other cases, return a 500 error
res.status(500).json(safeErrorSerialize(err));
//=> We can't recognize this error, pass to the next error handler.
next(err);
};
}

//=> Log it because that's unexpected
export function fatalErrorsHandler(): ErrorRequestHandler {
return (err: any, req: Request, res: ISentryResponse, next: NextFunction): void => {
//=> We don't know what this error is, return a 500 error
res.status(500).json(safeErrorSerialize(err, res.sentry));

//=> Log it because that's, well, unexpected
logger.error(err);

next();
};
}
8 changes: 7 additions & 1 deletion src/safe_error_serialize.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
export interface IPlainObjectError {
name: string;
message: string;
errorId?: string;
errors?: any[];
}

export function safeErrorSerialize(error: any): IPlainObjectError {
export function safeErrorSerialize(error: any, sentryErrorId?: string): IPlainObjectError {
const plainError: any = {};

//=> Error name
Expand All @@ -13,6 +14,11 @@ export function safeErrorSerialize(error: any): IPlainObjectError {
//=> Error message
plainError.message = error.message || 'Unknown error';

//=> Set error's ID if given
if (sentryErrorId) {
plainError.errorId = sentryErrorId;
}

//=> Serialize recursively sub-errors if any
if (error.errors) {
plainError.errors = error.errors.map(safeErrorSerialize);
Expand Down

0 comments on commit 271d808

Please sign in to comment.