UNPKG

@riyad_ahsan/typeorm-query-kit

Version:

A powerful query parser for TypeORM with filtering, sorting, pagination, and relation support

137 lines (136 loc) 4.67 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.queryParser = queryParser; function queryParser(req, res, next) { try { let { filter, range, sort, page, limit, relations } = req.query; req.queryOptions = { filter: parseFilter(filter), range: parseRange(range), sort: parseSort(sort), page: parsePage(page), limit: parseLimit(limit), relations: parseRelations(relations), config: req.entityConfig // Optional: can be set by middleware earlier }; next(); } catch (error) { next(error); } } function parseFilter(filter) { if (!filter) return {}; try { const parsed = typeof filter === 'string' ? JSON.parse(filter) : filter; validateFilter(parsed); return parsed; } catch (error) { throw new Error(`Invalid filter format: ${error.message}`); } } function validateFilter(filter) { const validOperators = ['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$like', '$like', '$in', '$nin', '$isNull']; for (const [, value] of Object.entries(filter)) { if (value && typeof value === 'object' && !Array.isArray(value)) { // Validate operator syntax for (const [operator, opValue] of Object.entries(value)) { if (operator.startsWith('$') && !validOperators.includes(operator)) { throw new Error(`Invalid operator '${operator}'. Valid operators: ${validOperators.join(', ')}`); } // Special validation for $isNull which expects boolean if (operator === '$isNull' && typeof opValue !== 'boolean') { throw new Error('$isNull operator requires a boolean value'); } } } } } function parseRange(range) { if (!range) return {}; try { const parsed = typeof range === 'string' ? JSON.parse(range) : range; validateRange(parsed); return parsed; } catch (error) { throw new Error(`Invalid range format: ${error.message}`); } } function validateRange(range) { if (typeof range !== 'object' || range === null) { throw new Error('Range must be an object'); } const validOperators = ['gt', 'gte', 'lt', 'lte']; for (const [field, conditions] of Object.entries(range)) { if (typeof conditions !== 'object' || conditions === null) { throw new Error(`Range conditions for ${field} must be an object`); } for (const [operator, value] of Object.entries(conditions)) { if (!validOperators.includes(operator)) { throw new Error(`Invalid range operator '${operator}'. Valid operators: ${validOperators.join(', ')}`); } if (typeof value !== 'number') { throw new Error(`Range value for ${field}.${operator} must be a number`); } } } } function parseSort(sort) { if (!sort) return undefined; try { const sortParsed = typeof sort === "string" ? JSON.parse(sort) : sort; const sortArray = Array.isArray(sortParsed) ? sortParsed : [sortParsed]; return sortArray.map((s) => { if (!s.field || typeof s.field !== "string") { throw new Error("Sort field is required and must be a string"); } return { field: s.field, order: s.order?.toUpperCase() === "DESC" ? "DESC" : "ASC", }; }); } catch (error) { throw new Error(`Invalid sort format: ${error.message}`); } } function parsePage(page) { const defaultPage = 1; if (!page) return defaultPage; const parsed = parseInt(String(page), 10); if (isNaN(parsed) || parsed < 1) { throw new Error('Page must be a positive integer'); } return parsed; } function parseLimit(limit) { const defaultLimit = 10; const maxLimit = 100; if (!limit) return defaultLimit; const parsed = parseInt(String(limit), 10); if (isNaN(parsed)) { throw new Error('Limit must be a number'); } if (parsed < 1) { throw new Error('Limit must be at least 1'); } return Math.min(parsed, maxLimit); } function parseRelations(relations) { if (!relations) return []; if (typeof relations === 'string') { return relations.split(',').map(r => r.trim()).filter(Boolean); } if (Array.isArray(relations)) { return relations.map(String).filter(Boolean); } return []; }