The max-100 cap is removed. for all list api

This commit is contained in:
Gowtham M 2026-07-21 17:52:23 +05:30
parent 472ccd7ec7
commit 81516a42d0
17 changed files with 73 additions and 49 deletions

View File

@ -34,6 +34,6 @@ Stack: Node.js 20+ · Express (CommonJS) · PostgreSQL 15+ · Prisma · Joi · W
## API
- Routes under `/api/v1/`; pagination `?page=1&limit=20` (max 100); dates ISO 8601 UTC
- Routes under `/api/v1/`; pagination `?page=1&limit=20` (no upper cap on limit); dates ISO 8601 UTC
- RBAC actions: `view`, `create`, `edit`, `delete`, `approve`, `export`
- Reference: `BACKEND_SETUP.md`, task checklist: `BACKEND_TASKS.md`

View File

@ -407,7 +407,7 @@ Each sub-master supports: `GET /` (list), `GET /:id`, `POST /`, `PUT /:id`, `DEL
## 7. API Conventions
- Versioning: `/api/v1/...`
- Pagination: `?page=1&limit=20` (cap limit at 100); response `meta: { page, limit, total }`
- Pagination: `?page=1&limit=20` (limit accepted as sent by FE, min 1, default 20); response `meta: { page, limit, total }`
- Filtering: `?status=...&search=...` per module
- Sorting: `?sort=-created_at` (`-` = descending)
- IDs: `BigInt` serialized as strings in JSON

View File

@ -113,7 +113,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: table_name
schema: { type: string }

View File

@ -690,7 +690,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -759,7 +759,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -833,7 +833,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -922,7 +922,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -991,7 +991,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1060,7 +1060,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string, description: Search by code or description }
@ -1129,7 +1129,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1223,7 +1223,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: type
schema: { type: string, enum: [PO, INVOICE] }
@ -1295,7 +1295,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1364,7 +1364,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1433,7 +1433,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1513,7 +1513,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1585,7 +1585,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1654,7 +1654,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1723,7 +1723,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1834,7 +1834,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }
@ -1929,7 +1929,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }

View File

@ -187,7 +187,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }

View File

@ -190,7 +190,7 @@ paths:
schema: { type: integer, default: 1 }
- in: query
name: limit
schema: { type: integer, default: 20, maximum: 100 }
schema: { type: integer, default: 20, minimum: 1 }
- in: query
name: search
schema: { type: string }

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const {
ASSET_CONDITIONS,
ASSET_STATUSES,
@ -116,8 +117,8 @@ const updateAssetSchema = Joi.object({
}).min(1);
const listAssetsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
status: Joi.string()
.valid(...ASSET_STATUSES)
@ -315,8 +316,8 @@ const depreciationCalculateSchema = Joi.object({
});
const myMaintenanceAssetsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
due_only: Joi.boolean().optional(),
status: Joi.string()
@ -334,13 +335,13 @@ const createMaintenanceLogSchema = Joi.object({
});
const listMaintenanceLogsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
});
const expiryAlertsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
days: Joi.number().integer().min(1).max(365).default(90),
type: Joi.string()
.valid(...ALERT_TYPES)
@ -348,8 +349,8 @@ const expiryAlertsQuerySchema = Joi.object({
});
const serviceAlertsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
status: Joi.string()
.valid(...SERVICE_ALERT_STATUSES)
.optional(),

View File

@ -1,8 +1,9 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const listAuditLogsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
table_name: Joi.string().max(100).trim().optional(),
record_id: Joi.number().integer().positive().optional(),
action: Joi.string().max(50).trim().optional(),

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const { GRN_STATUSES } = require('./grn.constants');
const grnItemSchema = Joi.object({
@ -47,8 +48,8 @@ const updateGrnSchema = Joi.object({
}).min(1);
const listGrnQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
status: Joi.string()
.valid(...GRN_STATUSES)

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../../utils/pagination.validation');
const MASTER_CODE_REGEX = /^[A-Za-z0-9\-_/]+$/;
const MASTER_NAME_REGEX = /^[A-Za-z0-9 \-_/&.()]+$/;
@ -63,8 +64,8 @@ const masterName = ({ max = 150, required = false } = {}) => {
};
const listQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
is_active: Joi.boolean().optional(),
});

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const { PO_STATUSES, VENDOR_TYPES } = require('./purchase-orders.constants');
const poItemSchema = Joi.object({
@ -84,8 +85,8 @@ const amendPurchaseOrderSchema = Joi.object({
});
const listPurchaseOrdersQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
status: Joi.string()
.valid(...PO_STATUSES)

View File

@ -1,9 +1,10 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const { ASSET_STATUSES } = require('../assets/assets.constants');
const depreciationReportQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().max(100).trim().allow('').optional(),
status: Joi.string()
.valid(...ASSET_STATUSES)

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const createRoleSchema = Joi.object({
name: Joi.string().max(100).required(),
@ -38,8 +39,8 @@ const permissionMatrixSchema = Joi.object({
});
const listRolesQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
is_active: Joi.boolean().optional(),
});

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const roleIdsSchema = Joi.array().items(Joi.number().integer().positive()).min(1);
@ -33,8 +34,8 @@ const updateUserSchema = Joi.object({
}).min(1);
const listUsersQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
status: Joi.string().valid('active', 'inactive', 'locked').optional(),
is_active: Joi.boolean().optional(),

View File

@ -1,4 +1,5 @@
const Joi = require('joi');
const { pageQuery, limitQuery } = require('../../utils/pagination.validation');
const {
GST_TREATMENT_VALUES,
SOURCE_OF_SUPPLY_VALUES,
@ -58,8 +59,8 @@ const vendorStatusSchema = Joi.object({
});
const listVendorsQuerySchema = Joi.object({
page: Joi.number().integer().min(1).default(1),
limit: Joi.number().integer().min(1).max(100).default(20),
page: pageQuery(),
limit: limitQuery(),
search: Joi.string().allow('').optional(),
status: Joi.string()
.valid(...vendorStatuses)

View File

@ -1,6 +1,7 @@
const getPagination = (query) => {
const page = Math.max(Number(query.page) || 1, 1);
const limit = Math.min(Math.max(Number(query.limit) || 20, 1), 100);
// Accept whatever positive limit FE sends (no upper cap).
const limit = Math.max(Number(query.limit) || 20, 1);
const skip = (page - 1) * limit;
return { page, limit, skip };
};

View File

@ -0,0 +1,14 @@
const Joi = require('joi');
/** Empty / missing page → 1. */
const pageQuery = () =>
Joi.number().integer().min(1).empty(['', null]).default(1);
/**
* Empty / missing limit 20.
* When FE sends a number, accept it as-is (no upper cap).
*/
const limitQuery = () =>
Joi.number().integer().min(1).empty(['', null]).default(20);
module.exports = { pageQuery, limitQuery };