Server-side datatable processing for Mongoose using aggregation pipelines.
- Node.js 18 or newer
- Mongoose 8.x (
mongooseis a peer dependency)
npm install mongoose-datatable mongooseRegister the plugin on a schema or globally:
import mongoose from 'mongoose';
import { datatablePlugin } from 'mongoose-datatable';
mongoose.plugin(datatablePlugin);The plugin adds the static method datatable(query, options?) to Mongoose models. dataTable remains a deprecated compatibility alias.
The main query fields are:
draw: request identifier returned unchanged in the response;columns: requested fields, projection expressions and search metadata;orderable: falseprevents ordering by that column;order: column indexes andasc/descdirection;start: page index;length: page size;search: optional global search;facets: optional indicator aggregations;enableUnfilteredInfo: whentrue, also calculaterecordsTotal.
Pagination intentionally treats start as a page index and converts it to the MongoDB skip offset with start * length.
Supported search operators include =, >, >=, ≥, <, ≤, range operators, $in and $nin.
{
draw,
recordsFiltered,
recordsTotal?,
data,
facets?,
}recordsTotal is only calculated when enableUnfilteredInfo === true. recordsFiltered always represents the number of records after the datatable filters.
model.datatable(query, options) supports:
conditions: base Mongoose conditions applied before datatable filtering;select: additional projection fields;unwind: aggregation unwind stages;logger: optional debug/warn logger.
Example:
const result = await Model.datatable(query, {
conditions: { archived: false },
});Hardening is opt-in so existing consumers keep their current behavior. Configure it either when registering the plugin or for an individual datatable() call.
const result = await Model.datatable(query, {
security: {
validatePaths: true,
allowObjectSearch: false,
allowedSearchOperators: ['=', '>', '>=', '≥', '<', '≤', '<>', '≤≥', '><', '≥≤', '$in', '$nin'],
allowedFacetOperators: ['count', 'sum', 'avg'],
allowedProjectionOperators: ['$size', '$ifNull'],
allowedFacetInfoOperators: ['$first'],
validateSelectableExpressionFields: true,
validateSelectableFacetFields: true,
maxRegexLength: 256,
},
});Available controls:
validatePaths: reject unsafe MongoDB paths, reserved object keys such as$whereor__proto__, and facet IDs reserved by the internal pipeline (data,recordsFiltered,recordsTotal);allowObjectSearch: whenfalse, reject raw object query values while keeping date-range{ from, to }values supported;allowedSearchOperators: allowlist search operators accepted from the request;allowedFacetOperators: allowlistcount,sumandavg;allowedProjectionOperators: recursively allowlist MongoDB operators used by client-provided projection expressions;allowedFacetInfoOperators: recursively allowlist MongoDB operators used by facetinfo;validateSelectableExpressionFields: prevent expressions from reading fields disabled withselect: false,dataTableSelect: falseordatatable.selectabled: false;validateSelectableFacetFields: prevent facet keys andsum/avgsource fields from reading fields disabled by the same schema selection rules;maxRegexLength: reject client-provided regular expressions longer than the configured positive character limit.
When security hardening is enabled, client-provided projection and facet info expressions reject MongoDB aggregation variables such as $$ROOT and $$CURRENT. This prevents expression variables from bypassing selectable-field validation.
Malformed typed search values now fail closed instead of silently removing the filter. Invalid ObjectIds, numbers, booleans, dates, ranges, $in / $nin shapes, and invalid regular expressions therefore return no matching rows rather than broadening the query. Arrays supplied for ObjectId, number, date, and boolean searches are normalized element by element.
Fields referenced only by facet info expressions are preserved in the aggregation projection when they resolve to selectable schema fields. Nested array-reference lookups preserve documents whose arrays are empty or missing.
If a security option is omitted, that specific restriction is not enabled. Schema-level and per-call security options are merged, with per-call values taking precedence.
A field is not exposed by the datatable when one of the following schema options disables selection:
select: falseor the historical options:
dataTableSelect: falsedatatable: { selectabled: false }Indicator facets support count, sum and avg operations. A facet may also expose an info aggregation value used by the frontend to render richer indicators.
npm test
npm run buildThe test command starts a temporary MongoDB 8 Docker container, runs the compiled Mocha suite with coverage, and cleans the container when it finishes.