Skip to content

Commit

Permalink
Merge pull request #2 from weather-blade/errors-middleware
Browse files Browse the repository at this point in the history
create middleware for handling errors
  • Loading branch information
Bladesheng authored Mar 9, 2024
2 parents e19b6c8 + 080c04e commit 767acf6
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 30 deletions.
63 changes: 33 additions & 30 deletions src/controllers/readings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ReadingsValidation } from '../validations/readings.validation.js';
import { redisClient } from '../db/redis.js';
import { UtilFns } from '../utils/functions.js';
import { lttb } from '../utils/lttb.js';
import { AppError } from '../exceptions/AppError.js';
import type { Request, Response, NextFunction } from 'express';

const WEEK_SECONDS = 604800;
Expand All @@ -26,13 +27,12 @@ export class ReadingsController {
});

if (reading === null) {
return res.status(404).send('404 Reading not Found');
throw new AppError(404, 'Reading not found');
}

res.json(reading);
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -44,7 +44,7 @@ export class ReadingsController {
const endTime = new Date(String(req.query.end));

if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
return res.status(400).send('400 Bad Request (use ISO 8601 time format)');
throw new AppError(400, 'Bad request (use ISO 8601 time format)');
}

const readings = await prisma.readings.findMany({
Expand All @@ -59,8 +59,7 @@ export class ReadingsController {

res.json(readings);
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -72,8 +71,7 @@ export class ReadingsController {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.error(errors);
return res.status(400).json(errors);
throw new AppError(400, 'Bad request (wrong year / month format)', errors);
}

const year = parseInt(req.query.year as string);
Expand All @@ -82,7 +80,7 @@ export class ReadingsController {
const { firstDay, lastDay } = UtilFns.getFirstLastDay(year, month);

if (isNaN(firstDay.getTime()) || isNaN(lastDay.getTime())) {
return res.status(400).send('400 Bad Request (wrong year / month format)');
throw new AppError(400, 'Bad request (wrong year / month format)');
}

const cacheName = `month-full-${firstDay.getUTCFullYear()}-${firstDay.getUTCMonth()}`;
Expand Down Expand Up @@ -115,8 +113,7 @@ export class ReadingsController {
EX: WEEK_SECONDS, // expire after 7 days
});
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -128,8 +125,7 @@ export class ReadingsController {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.error(errors);
return res.status(400).json(errors);
throw new AppError(400, 'Bad request (wrong year / month format)', errors);
}

const year = parseInt(req.query.year as string);
Expand All @@ -138,7 +134,7 @@ export class ReadingsController {
const { firstDay, lastDay } = UtilFns.getFirstLastDay(year, month);

if (isNaN(firstDay.getTime()) || isNaN(lastDay.getTime())) {
return res.status(400).send('400 Bad Request (wrong year / month format)');
throw new AppError(400, 'Bad request (wrong year / month format)');
}

const cacheName = `month-decimated-${firstDay.getUTCFullYear()}-${firstDay.getUTCMonth()}`;
Expand Down Expand Up @@ -210,8 +206,7 @@ export class ReadingsController {
EX: WEEK_SECONDS, // expire after 7 days
});
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand Down Expand Up @@ -246,8 +241,7 @@ export class ReadingsController {
});
}
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -263,8 +257,7 @@ export class ReadingsController {
const errors = validationResult(req);

if (!errors.isEmpty()) {
console.log(errors);
return res.status(400).json(errors);
throw new AppError(400, 'Bad request', errors);
}

const temperature_BMP = parseFloat(req.body.temperature_BMP);
Expand Down Expand Up @@ -306,8 +299,7 @@ export class ReadingsController {
redisClient.del(cacheNameFull);
redisClient.del(cacheNameDecimated);
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -324,8 +316,7 @@ export class ReadingsController {
const errors = validationResult(req);

if (!errors.isEmpty()) {
console.log(errors);
return res.status(400).json(errors);
throw new AppError(400, 'Bad request', errors);
}

const id = parseInt(req.params.id);
Expand Down Expand Up @@ -359,8 +350,7 @@ export class ReadingsController {

res.json(result);
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand All @@ -369,15 +359,29 @@ export class ReadingsController {

public static deleteReading = [
checkSchema(ReadingsValidation.readingId),

async (req: Request, res: Response, next: NextFunction) => {
try {
const errors = validationResult(req);

if (!errors.isEmpty()) {
throw new AppError(400, 'Bad request', errors);
}

next();
} catch (error) {
next(error);
}
},

checkSchema(ReadingsValidation.readingIdExists),

async (req: Request, res: Response, next: NextFunction) => {
try {
const errors = validationResult(req);

if (!errors.isEmpty()) {
console.log(errors);
return res.status(400).json(errors);
throw new AppError(404, 'Reading not found', errors);
}

const id = parseInt(req.params.id);
Expand All @@ -388,8 +392,7 @@ export class ReadingsController {

res.json(results);
} catch (error) {
console.error(error);
return res.status(500).send('500 Internal Server Error');
next(error);
}
},
];
Expand Down
10 changes: 10 additions & 0 deletions src/exceptions/AppError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class AppError extends Error {
public statusCode: number;
public errors?: object;

constructor(statusCode: number, message: string, errors: object | undefined = undefined) {
super(message);
this.statusCode = statusCode;
this.errors = errors;
}
}
47 changes: 47 additions & 0 deletions src/exceptions/ErrorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { Response, Request, NextFunction } from 'express';
import { AppError } from './AppError.js';

type ErrorResponse = {
statusCode: number;
message: string;
errors?: object;
};

export class ErrorHandler {
public static handleError(
err: AppError | Error,
req: Request,
res: Response,
next: NextFunction
) {
console.log('Error', err);

if (err instanceof AppError) {
ErrorHandler.handleKnownError(err, req, res);
} else {
ErrorHandler.handleUnknownError(err, req, res);
}
}

private static handleKnownError(err: AppError, req: Request, res: Response) {
const { statusCode, message } = err;

const response: ErrorResponse = {
statusCode,
message,
};

if (err.errors) {
response.errors = err.errors;
}

return res.status(statusCode).json(response);
}

private static handleUnknownError(err: Error, req: Request, res: Response) {
res.status(500).json({
statusCode: 500,
message: 'Internal server error',
});
}
}
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import compression from 'compression';
import apiRouter from './routes/api.route.js';
import { cors } from './middleware/cors.js';
import { notFound } from './middleware/notFound.js';
import { ErrorHandler } from './exceptions/ErrorHandler.js';

dotenv.config();

Expand All @@ -22,6 +23,8 @@ app.use(cors);

app.use('/api', apiRouter);

app.use(ErrorHandler.handleError);

app.use(notFound);

const port = process.env.PORT || 8080;
Expand Down

0 comments on commit 767acf6

Please sign in to comment.