mirror of
https://github.com/Shineii86/AniKotoAPI.git
synced 2026-10-22 10:55:10 +00:00
feat: add pagination metadata to all list endpoints
- Add pagination.helper.js with addPaginationMeta function - Add pagination info to: most-popular, search, new-release, newly-added, latest-updated, az-list, filter, category, status - Pagination includes: currentPage, totalPages, totalItems, itemsPerPage, hasNext, hasPrev - Fix creatorInfo middleware to include message field
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { extractAzList } from "../extractors/azList.extractor.js";
|
import { extractAzList } from "../extractors/azList.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: A-Z LIST
|
// CONTROLLER: A-Z LIST
|
||||||
@@ -51,8 +52,9 @@ const getAzList = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractAzList(letter, page);
|
const data = await extractAzList(letter, page);
|
||||||
setCache(cacheKey, data, TTL.genres);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.genres);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { extractCategory } from "../extractors/category.extractor.js";
|
import { extractCategory } from "../extractors/category.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: CATEGORY
|
// CONTROLLER: CATEGORY
|
||||||
@@ -55,8 +56,9 @@ const getCategory = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractCategory(category, page);
|
const data = await extractCategory(category, page);
|
||||||
setCache(cacheKey, data, TTL.genres);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.genres);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { extractFilter } from "../extractors/filter.extractor.js";
|
import { extractFilter } from "../extractors/filter.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: FILTER
|
// CONTROLLER: FILTER
|
||||||
@@ -47,6 +48,7 @@ import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
|||||||
const getFilter = async (req, res, next) => {
|
const getFilter = async (req, res, next) => {
|
||||||
try {
|
try {
|
||||||
const params = req.query;
|
const params = req.query;
|
||||||
|
const page = params.page || 1;
|
||||||
const cacheKey = `filter_${JSON.stringify(params, Object.keys(params).sort())}`;
|
const cacheKey = `filter_${JSON.stringify(params, Object.keys(params).sort())}`;
|
||||||
const cached = getCache(cacheKey);
|
const cached = getCache(cacheKey);
|
||||||
if (cached) {
|
if (cached) {
|
||||||
@@ -57,8 +59,9 @@ const getFilter = async (req, res, next) => {
|
|||||||
if (Array.isArray(params.type)) params.type = params.type.join(",");
|
if (Array.isArray(params.type)) params.type = params.type.join(",");
|
||||||
if (Array.isArray(params.status)) params.status = params.status.join(",");
|
if (Array.isArray(params.status)) params.status = params.status.join(",");
|
||||||
const data = await extractFilter(params);
|
const data = await extractFilter(params);
|
||||||
setCache(cacheKey, data, TTL.genres);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.genres);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
import { extractNewRelease, extractNewlyAdded, extractLatestUpdated } from "../extractors/newRelease.extractor.js";
|
import { extractNewRelease, extractNewlyAdded, extractLatestUpdated } from "../extractors/newRelease.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: NEW RELEASE
|
// CONTROLLER: NEW RELEASE
|
||||||
@@ -48,8 +49,9 @@ const getNewRelease = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractNewRelease(page);
|
const data = await extractNewRelease(page);
|
||||||
setCache(cacheKey, data, TTL.default);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.default);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -83,8 +85,9 @@ const getNewlyAdded = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractNewlyAdded(page);
|
const data = await extractNewlyAdded(page);
|
||||||
setCache(cacheKey, data, TTL.default);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.default);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
@@ -117,8 +120,9 @@ const getLatestUpdated = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractLatestUpdated(page);
|
const data = await extractLatestUpdated(page);
|
||||||
setCache(cacheKey, data, TTL.default);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.default);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { extractPopular } from "../extractors/popular.extractor.js";
|
import { extractPopular } from "../extractors/popular.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: POPULAR
|
// CONTROLLER: POPULAR
|
||||||
@@ -47,8 +48,9 @@ const getPopular = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractPopular(page);
|
const data = await extractPopular(page);
|
||||||
setCache(cacheKey, data, TTL.default);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.default);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
import { extractSearchResults, extractSearchSuggestions } from "../extractors/search.extractor.js";
|
import { extractSearchResults, extractSearchSuggestions } from "../extractors/search.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: SEARCH RESULTS
|
// CONTROLLER: SEARCH RESULTS
|
||||||
@@ -52,8 +53,9 @@ const getSearchResults = async (req, res, next) => {
|
|||||||
return res.json({ success: true, results: cached });
|
return res.json({ success: true, results: cached });
|
||||||
}
|
}
|
||||||
const data = await extractSearchResults(keyword, page);
|
const data = await extractSearchResults(keyword, page);
|
||||||
setCache(cacheKey, data, TTL.search);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.search);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
import { extractStatus } from "../extractors/status.extractor.js";
|
import { extractStatus } from "../extractors/status.extractor.js";
|
||||||
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
import { getCache, setCache, TTL } from "../helper/cache.helper.js";
|
||||||
|
import { addPaginationMeta } from "../helper/pagination.helper.js";
|
||||||
|
|
||||||
// ══════════════════════════════════════════════════════════════
|
// ══════════════════════════════════════════════════════════════
|
||||||
// CONTROLLER: STATUS
|
// CONTROLLER: STATUS
|
||||||
@@ -62,8 +63,9 @@ const getStatus = async (req, res, next) => {
|
|||||||
};
|
};
|
||||||
const mappedStatus = statusMap[status] || status;
|
const mappedStatus = statusMap[status] || status;
|
||||||
const data = await extractStatus(mappedStatus, page);
|
const data = await extractStatus(mappedStatus, page);
|
||||||
setCache(cacheKey, data, TTL.genres);
|
const response = addPaginationMeta(data, page);
|
||||||
res.json({ success: true, results: data });
|
setCache(cacheKey, response, TTL.genres);
|
||||||
|
res.json({ success: true, results: response });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* ======= • ======= • ======= • ======= • =======• =======
|
||||||
|
* AniKotoAPI — pagination.helper.js
|
||||||
|
* Repository: https://github.com/Shineii86/AniKotoAPI
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Utility function to add pagination metadata to API responses.
|
||||||
|
* Provides standardized pagination info for all paginated endpoints.
|
||||||
|
*
|
||||||
|
* @exports
|
||||||
|
* addPaginationMeta
|
||||||
|
*
|
||||||
|
* @author Shinei Nouzen
|
||||||
|
* @license MIT
|
||||||
|
* ======= • ======= • ======= • ======= • =======• =======
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds pagination metadata to a response object.
|
||||||
|
*
|
||||||
|
* @param {object} data - The data object containing `data` array and optionally `totalPages`
|
||||||
|
* @param {number} currentPage - Current page number (1-indexed)
|
||||||
|
* @param {number} [itemsPerPage=30] - Number of items per page
|
||||||
|
* @returns {object} Object with data and pagination metadata
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const response = addPaginationMeta({ data: [...], totalPages: 5 }, 1, 30);
|
||||||
|
* // response = {
|
||||||
|
* // data: [...],
|
||||||
|
* // pagination: {
|
||||||
|
* // currentPage: 1,
|
||||||
|
* // totalPages: 5,
|
||||||
|
* // totalItems: 150,
|
||||||
|
* // itemsPerPage: 30,
|
||||||
|
* // hasNext: true,
|
||||||
|
* // hasPrev: false
|
||||||
|
* // }
|
||||||
|
* // }
|
||||||
|
*/
|
||||||
|
const addPaginationMeta = (data, currentPage, itemsPerPage = 30) => {
|
||||||
|
const totalPages = data.totalPages || 1;
|
||||||
|
const page = parseInt(currentPage) || 1;
|
||||||
|
const totalItems = data.data ? data.data.length * totalPages : 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...data,
|
||||||
|
pagination: {
|
||||||
|
currentPage: page,
|
||||||
|
totalPages: totalPages,
|
||||||
|
totalItems: totalItems,
|
||||||
|
itemsPerPage: itemsPerPage,
|
||||||
|
hasNext: page < totalPages,
|
||||||
|
hasPrev: page > 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export { addPaginationMeta };
|
||||||
|
// ══════════════════════════════════════════════════════════════ END: pagination.helper.js
|
||||||
@@ -61,6 +61,7 @@ const addCreatorInfo = (req, res, next) => {
|
|||||||
creator: creatorInfo.creator,
|
creator: creatorInfo.creator,
|
||||||
github: creatorInfo.github,
|
github: creatorInfo.github,
|
||||||
telegram: creatorInfo.telegram,
|
telegram: creatorInfo.telegram,
|
||||||
|
message: creatorInfo.message,
|
||||||
timestamp: new Date().toLocaleString("en-IN", { timeZone: "Asia/Kolkata", hour12: true }),
|
timestamp: new Date().toLocaleString("en-IN", { timeZone: "Asia/Kolkata", hour12: true }),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user