@kitstack/nest-powertools
Version:
A comprehensive collection of NestJS powertools, decorators, and utilities to supercharge your backend development
343 lines • 11.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PaginationHelper = exports.ResponseFormatter = void 0;
const enums_1 = require("../types/enums");
class ResponseFormatter {
static generateRequestId() {
return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
static getTimestamp() {
return new Date().toISOString();
}
static success(data, message, metadata) {
return {
success: true,
status: enums_1.ResponseStatus.SUCCESS,
code: enums_1.ResponseCodes.OPERATION_SUCCESSFUL,
data,
message,
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
metadata,
};
}
static error(message, code = enums_1.ResponseCodes.INTERNAL_ERROR, details, field) {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code,
error: {
message,
details,
field,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static warning(data, message, code = enums_1.ResponseCodes.OPERATION_SUCCESSFUL, metadata) {
return {
success: true,
status: enums_1.ResponseStatus.WARNING,
code,
data,
message,
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
metadata,
};
}
static paginated(data, total, page, limit, message) {
const totalPages = Math.ceil(total / limit);
const hasNext = page < totalPages;
const hasPrevious = page > 1;
return {
data,
pagination: {
total,
page,
limit,
totalPages,
hasNext,
hasPrevious,
},
status: enums_1.ResponseStatus.SUCCESS,
timestamp: this.getTimestamp(),
message,
};
}
static validationError(errors) {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.VALIDATION_FAILED,
error: {
message: 'Validation failed',
details: errors,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static authenticationError(message = 'Authentication required') {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.AUTHENTICATION_REQUIRED,
error: {
message,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static authorizationError(message = 'Insufficient permissions') {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.AUTHORIZATION_FAILED,
error: {
message,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static notFound(resource) {
const message = resource ? `${resource} not found` : 'Resource not found';
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.RESOURCE_NOT_FOUND,
error: {
message,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static conflict(message = 'Resource conflict') {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.RESOURCE_CONFLICT,
error: {
message,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static rateLimitExceeded(message = 'Rate limit exceeded') {
return {
success: false,
status: enums_1.ResponseStatus.ERROR,
code: enums_1.ResponseCodes.RATE_LIMIT_EXCEEDED,
error: {
message,
},
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
};
}
static custom(success, status, code, data, message, metadata) {
return {
success,
status,
code,
data,
message,
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
metadata,
};
}
static transform(data, options) {
const { message, status = enums_1.ResponseStatus.SUCCESS, code = enums_1.ResponseCodes.OPERATION_SUCCESSFUL, metadata, } = options || {};
return {
success: true,
status,
code,
data,
message,
timestamp: this.getTimestamp(),
requestId: this.generateRequestId(),
metadata,
};
}
}
exports.ResponseFormatter = ResponseFormatter;
class PaginationHelper {
static paginate(data, total, pagination, message) {
const { page, limit, sortBy, sortOrder, search, filters, offset } = pagination;
const totalPages = limit === -1 ? 1 : Math.ceil(total / limit);
const hasNext = limit === -1 ? false : page < totalPages;
const hasPrevious = page > 1;
return {
data,
pagination: {
total,
page,
limit,
totalPages,
hasNext,
hasPrevious,
offset,
sortBy,
sortOrder,
search,
filters,
},
status: enums_1.ResponseStatus.SUCCESS,
timestamp: new Date().toISOString(),
message,
};
}
static getSkipTake(pagination) {
const { page, limit } = pagination;
if (limit === -1) {
return { skip: 0, take: -1 };
}
const skip = (page - 1) * limit;
return { skip, take: limit };
}
static getOffsetLimit(pagination) {
const { page, limit } = pagination;
if (limit === -1) {
return { offset: 0, limit: -1 };
}
const offset = (page - 1) * limit;
return { offset, limit };
}
static createLinks(pagination, total, baseUrl) {
const { page, limit, sortBy, sortOrder, search, filters } = pagination;
const totalPages = limit === -1 ? 1 : Math.ceil(total / limit);
const buildUrl = (targetPage) => {
const params = new URLSearchParams();
params.set('page', targetPage.toString());
params.set('limit', limit.toString());
params.set('sortBy', sortBy);
params.set('sortOrder', sortOrder);
if (search)
params.set('search', search);
if (filters)
params.set('filters', JSON.stringify(filters));
return `${baseUrl}?${params.toString()}`;
};
const links = {
first: buildUrl(1),
last: buildUrl(totalPages),
self: buildUrl(page),
};
if (page > 1) {
links.prev = buildUrl(page - 1);
}
if (page < totalPages && limit !== -1) {
links.next = buildUrl(page + 1);
}
return links;
}
static validate(pagination, options = {}) {
const errors = [];
const { page, limit, sortBy, search } = pagination;
if (page < 1) {
errors.push('Page must be greater than 0');
}
if (limit !== -1) {
if (limit < 1) {
errors.push('Limit must be greater than 0');
}
if (options.maxLimit && limit > options.maxLimit) {
errors.push(`Limit cannot exceed ${options.maxLimit}`);
}
}
if (options.allowedSortFields &&
!options.allowedSortFields.includes(sortBy)) {
errors.push(`Invalid sort field: ${sortBy}. Allowed fields: ${options.allowedSortFields.join(', ')}`);
}
if (options.requireSearch && (!search || search.trim().length === 0)) {
errors.push('Search parameter is required');
}
return {
valid: errors.length === 0,
errors,
};
}
static forPrisma(pagination) {
const { skip, take } = this.getSkipTake(pagination);
const { sortBy, sortOrder } = pagination;
return {
skip: skip,
take: take === -1 ? undefined : take,
orderBy: {
[sortBy]: sortOrder.toLowerCase(),
},
};
}
static forTypeORM(pagination) {
const { skip, take } = this.getSkipTake(pagination);
const { sortBy, sortOrder } = pagination;
return {
skip: skip,
take: take === -1 ? undefined : take,
order: {
[sortBy]: sortOrder,
},
};
}
static forMongoose(pagination) {
const { skip, take } = this.getSkipTake(pagination);
const { sortBy, sortOrder } = pagination;
const sort = { [sortBy]: sortOrder === 'ASC' ? 1 : -1 };
return {
skip,
limit: take === -1 ? 0 : take,
sort,
};
}
static createCursor(data, cursorField, pagination) {
const { limit } = pagination;
const hasNext = data.length > limit;
const hasPrevious = false;
const resultData = hasNext ? data.slice(0, limit) : data;
const nextCursor = hasNext && resultData.length > 0
? String(resultData[resultData.length - 1][cursorField])
: undefined;
return {
data: resultData,
hasNext,
hasPrevious,
nextCursor,
};
}
static getStats(pagination, total) {
const { page, limit } = pagination;
if (limit === -1) {
return {
totalItems: total,
totalPages: 1,
currentPage: 1,
itemsPerPage: total,
startIndex: 1,
endIndex: total,
isFirstPage: true,
isLastPage: true,
};
}
const totalPages = Math.ceil(total / limit);
const startIndex = (page - 1) * limit + 1;
const endIndex = Math.min(page * limit, total);
return {
totalItems: total,
totalPages,
currentPage: page,
itemsPerPage: limit,
startIndex,
endIndex,
isFirstPage: page === 1,
isLastPage: page === totalPages,
};
}
}
exports.PaginationHelper = PaginationHelper;
//# sourceMappingURL=response-formatter.js.map