npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mongoose-query-find

v2.1.1

Published

QueryFind is a TypeScript utility for Mongoose that simplifies building dynamic queries. It supports advanced filtering, global search across multiple fields, sorting, field selection, and pagination for MongoDB documents, making API query handling more e

Readme

mongoose-query-find

npm version license mongoose peer

A fluent, chainable query builder for Mongoose that handles filtering, global search, sorting, field projection, population, and pagination — all driven directly from URL query parameters with zero boilerplate.


Table of Contents


Installation

npm install mongoose-query-find
yarn add mongoose-query-find
pnpm add mongoose-query-find

Peer dependency: requires mongoose ^8 or mongoose ^9 installed in your project.


Quick Start

import QueryFind from 'mongoose-query-find';
import UserModel from './models/user';

const result = await new QueryFind(UserModel.find(), req.query)
  .filter()
  .globalFilter(['name', 'email'])
  .sort()
  .limitFields('-password -__v')
  .paginate();

result will look like:

{
  "data": [...],
  "total": 84,
  "page": 2,
  "totalPages": 9,
  "limit": 10
}

Constructor

new QueryFind(query, queryString);

| Parameter | Type | Description | | ------------- | ----------------------------------- | --------------------------------------------- | | query | Query<TRawDocType[], TRawDocType> | A Mongoose query, e.g. Model.find() | | queryString | QueryParams | The parsed URL query object, e.g. req.query |


Builder Methods

All builder methods return this, so they are fully chainable in any order.

.filter()

Parses the URL query string into a Mongoose filter. Automatically:

  • Strips reserved keys (page, limit, sort, fields, q)
  • Converts comparison operator names to MongoDB $ syntax (gt$gt, lte$lte, etc.)
  • Coerces string booleans to real booleans ("true"true, "false"false)
  • Coerces date-like string values to Date instances for fields named createdAt, updatedAt, deletedAt, date, or Date

Supported operators: eq, ne, gt, gte, lt, lte, in, nin

GET /users?age[gte]=18&isActive=true&role=admin
.filter()
// → { age: { $gte: 18 }, isActive: true, role: 'admin' }

.globalFilter(fields: string[])

Adds a case-insensitive $or regex search across the specified fields when the q query parameter is present. If q is absent, this method is a no-op.

GET /users?q=john
.globalFilter(['name', 'email'])
// → { $or: [{ name: /john/i }, { email: /john/i }] }

.sort()

Applies sort order from the sort query parameter. Prefix a field with - for descending order. Multiple fields are comma-separated.

GET /users?sort=-createdAt,name
.sort()
// → sorts by createdAt DESC, then name ASC

Defaults to { createdAt: -1 } when the sort param is absent.


.limitFields(defaultFields?: string)

Controls which fields are returned (projection). Uses the fields query param when present, otherwise falls back to defaultFields.

Priority order:

  1. ?fields= query param — always wins when present
  2. defaultFields argument — used as fallback when no query param
  3. No projection at all when both are absent (all fields returned)
GET /users?fields=name,email,role
.limitFields('-password -__v')
// With ?fields=name,email,role  → selects only name, email, role
// Without ?fields               → excludes password and __v

.populate(path: string | PopulateOptions, select?: string)

Registers a populate directive. Can be called multiple times to populate multiple paths — each call appends to the internal list. All registered populates are applied inside paginate() after the find query is built, so core filter / sort / pagination logic is completely untouched.

Accepts the same arguments as Mongoose's own .populate():

// Plain path string
.populate('author')

// Path + select string
.populate('author', 'name email')

// Full PopulateOptions object
.populate({ path: 'comments', select: 'text createdAt', match: { visible: true } })

// Multiple calls — each appends to the list
.populate('author')
.populate({ path: 'comments', select: 'text createdAt' })

Terminal Method

.paginate()

Executes the query and returns a Promise<PaginatedResult<T>>. Makes exactly two database round-trips:

  1. countDocuments — counts total matching documents (uses index scan)
  2. find — fetches the requested page with sort, skip, limit, projection, and any registered populates applied
GET /users?page=2&limit=20

Returns:

{
  data: T[];         // Documents for the current page
  total: number;     // Total matching documents across all pages
  page: number;      // Current page (auto-corrects to 1 if out of range)
  totalPages: number;
  limit: number;
}

If the requested page exceeds totalPages (e.g. after a deletion), page 1 is returned automatically so the caller always receives valid data.

Defaults: page=1, limit=10


Query Parameter Reference

| Parameter | Example | Description | | ----------- | ------------------------- | ------------------------------------------------- | | page | ?page=3 | Page number (default: 1, min: 1) | | limit | ?limit=25 | Documents per page (default: 10, min: 1) | | sort | ?sort=-createdAt,name | Sort fields; prefix - for descending | | fields | ?fields=name,email | Comma-separated fields to include in the response | | q | ?q=john | Global search term (used by .globalFilter()) | | (any key) | ?role=admin&age[gte]=18 | Field-level filters processed by .filter() |


Full Example (Express)

import { Request, Response } from 'express';
import QueryFind from 'mongoose-query-find';
import UserModel from '../models/user';

export const getUsers = async (req: Request, res: Response) => {
  const result = await new QueryFind(UserModel.find(), req.query)
    .filter()
    .globalFilter(['name', 'email', 'username'])
    .sort()
    .limitFields('-password -__v')
    .populate('role', 'name permissions')
    .paginate();

  res.json({
    status: 'success',
    ...result,
  });
};

Example requests:

# Page 2, 15 per page, only active admins sorted by name
GET /users?page=2&limit=15&role=admin&isActive=true&sort=name

# Search "alice" across name, email, and username
GET /users?q=alice

# Users older than 25, return only name and email
GET /users?age[gt]=25&fields=name,email

# Filter by date range
GET /users?createdAt[$gte]=2024-01-01&createdAt[$lte]=2024-12-31

# Combined: search + filter + sort + pagination
GET /users?q=john&role=editor&sort=-createdAt&page=1&limit=5

TypeScript Types

Both types are exported and available for use in your own code:

import QueryFind, { QueryParams, PaginatedResult } from 'mongoose-query-find';
interface QueryParams {
  page?: string;
  limit?: string;
  sort?: string;
  fields?: string;
  q?: string;
  [key: string]: unknown;
}

interface PaginatedResult<T> {
  data: T[];
  total: number;
  page: number;
  totalPages: number;
  limit: number;
}

Links


License

ISC © [email protected]