Skip to content

Commit

Permalink
perf: ⚡ better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
doralteres committed Sep 6, 2023
1 parent 45e72c4 commit f7f7939
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 19 deletions.
3 changes: 2 additions & 1 deletion src/middleware/body.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {NextFunction, Request, Response} from 'express';
import {customFields} from '../types';
import {isIncludeExcludeMatchCriteria} from './config';
import {crudError} from '../utils';

export const checkBodyFields = (bodyFields: customFields = []) => {
return async (req: Request, res: Response, next: NextFunction) => {
Expand All @@ -14,7 +15,7 @@ export const checkBodyFields = (bodyFields: customFields = []) => {
if (!result) {
res
.status(400)
.send(`Can't use [${problematicFields.join(', ')}] in body`);
.json(crudError(`Can't use [${problematicFields.join(', ')}] in body`));
} else {
next();
}
Expand Down
19 changes: 14 additions & 5 deletions src/middleware/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import {FindAndCountOptions} from 'sequelize';
import {customFields} from '../types';
import {NextFunction, Request, Response} from 'express';
import {isIncludeExcludeMatchCriteria} from './config';
import {crudError} from '../utils';

export const buildOptionsFromQueryParams = (
q: Record<string, any>
q: Record<string, any>,
maxLimit?: number
): FindAndCountOptions<unknown> => {
const {_sort, _order, _start, _end, ...where} = q;

const queryLimit = _end
? parseInt(_end) - (parseInt(_start) || 0)
: undefined;
return {
where,
limit: _end ? parseInt(_end) - (parseInt(_start) || 0) : undefined,
limit:
queryLimit && (!maxLimit || maxLimit >= queryLimit)
? queryLimit
: maxLimit,
offset: parseInt(_start) || 0,
order: _sort ? [[_sort, _order || 'DESC']] : undefined,
};
Expand All @@ -34,7 +41,9 @@ export const checkFilterableFields = (filterableFields: customFields = []) => {
) {
res
.status(400)
.send(`Can't filter by fields [${Object.keys(where).join(', ')}]`);
.json(
crudError(`Can't filter by fields [${Object.keys(where).join(', ')}]`)
);
} else {
next();
}
Expand All @@ -55,7 +64,7 @@ export const checkSortableFields = (sortableFields: customFields = []) => {
)
).result
) {
res.status(400).send(`Can't sort by field [${_sort}]`);
res.status(400).json(crudError(`Can't sort by field [${_sort}]`));
} else {
next();
}
Expand Down
7 changes: 4 additions & 3 deletions src/routes/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
runCustomMiddleware,
} from '../middleware/config';
import {checkBodyFields} from '../middleware/body';
import {getSequelizeErrorMessage} from '../utils';

const createRoute = (
model: pureModelType,
Expand All @@ -25,13 +26,13 @@ const createRoute = (
res
);

const data = await model.findByPk(req.params.resourceId, {
const data = await model.create(req.body, {
...options,
});
res.json(data);
res.status(201).json(data);
} catch (error) {
console.error(error);
res.status(500).send(error);
res.status(500).json(getSequelizeErrorMessage(error));
}
}
);
Expand Down
19 changes: 17 additions & 2 deletions src/routes/getList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@ import {Router} from 'express';
import {getListOptions, pureModelType} from '../types';
import {
buildOptionsFromConfig,
getFieldValue,
runCustomMiddleware,
} from '../middleware/config';
import {
buildOptionsFromQueryParams,
checkFilterableFields,
checkSortableFields,
} from '../middleware/query';
import {SequelizeScopeError} from 'sequelize';
import {getSequelizeErrorMessage} from '../utils';

const getListRoute = (
model: pureModelType,
Expand All @@ -35,7 +38,19 @@ const getListRoute = (
req,
res
);
const queryOptions = buildOptionsFromQueryParams(req.query);
const queryOptions = buildOptionsFromQueryParams(
req.query,
await getFieldValue(config.limit, req, res)
);
console.log({
queryOptions,
total: {
...options,
...queryOptions,
where: {...options.where, ...queryOptions.where},
},
});

if (pagination) {
const {rows, count} = await model.findAndCountAll({
...options,
Expand All @@ -54,7 +69,7 @@ const getListRoute = (
}
} catch (error) {
console.error(error);
res.status(500).send(error);
res.status(500).json(getSequelizeErrorMessage(error));
}
}
);
Expand Down
9 changes: 7 additions & 2 deletions src/routes/getOne.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
buildOptionsFromConfig,
runCustomMiddleware,
} from '../middleware/config';
import {getSequelizeErrorMessage} from '../utils';

const getOneRoute = (
model: pureModelType,
Expand All @@ -26,10 +27,14 @@ const getOneRoute = (
const data = await model.findByPk(req.params.resourceId, {
...options,
});
res.json(data);
if (data) {
res.json(data);
} else {
res.sendStatus(404);
}
} catch (error) {
console.error(error);
res.status(500).send(error);
res.status(500).json(getSequelizeErrorMessage(error));
}
}
);
Expand Down
13 changes: 7 additions & 6 deletions src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,20 @@ const buildModelRoutes = (
) => {
const router = Router();
const {model: m, operations} = config;

const model = getModel(sequelize, m);
console.group(`Model [${model.name}]`);
if (operations.getList) {
getListRoute(model, router, operations.getList);
console.log('GET', `/${path}`, '[getList]');
}
if (operations.create) {
createRoute(model, router, operations.create);
console.log('POST', `/${path}`, '[create]');
console.log('GET', path, '[getList]');
}
if (operations.getOne) {
getOneRoute(model, router, operations.getOne);
console.log('GET', `/${path}/:resourceId`, '[getOne]');
console.log('GET', `${path}/:resourceId`, '[getOne]');
}
if (operations.create) {
createRoute(model, router, operations.create);
console.log('POST', path, '[create]');
}

console.groupEnd();
Expand Down
40 changes: 40 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,43 @@ export const getModel = (sequelize: Sequelize, model: modelType) =>

export const getPath = (path: string) =>
path.startsWith('/') ? path : `/${path}`;

const findVal = (object: unknown, key: string): unknown => {
if (typeof object === 'object') {
if (object && key in object) {
// @ts-expect-error we know 'key' exist at this point.
return object[key];
}
for (const k in object) {
// @ts-expect-error we know 'k' exist at this point.
const val = object[k];
if (typeof val === 'object') {
const res = findVal(val, key);
if (res) {
return res;
}
}
}
return undefined;
} else {
return undefined;
}
};

const getSequelizeErrorMessageCore = (error: unknown) => {
const original = findVal(error, 'original');
if (original && typeof original === 'object' && 'message' in original) {
return original.message;
}
return findVal(error, 'message') || 'Internal Server Error';
};

export const crudError = (message: unknown) => ({
success: false,
message,
});

export const getSequelizeErrorMessage = (error: unknown) => {
const message = getSequelizeErrorMessageCore(error);
return crudError(typeof message === 'string' ? message.split('\n') : message);
};

0 comments on commit f7f7939

Please sign in to comment.