npm package: @luikingkit/req2mongo
Description: Express middleware for parsing HTTP request query parameters into MongoDB-compatible query objects.
Author: Lui King Kit
Latest version: 1.0.0

# Req2Mongo

**req2mongo** is an Express middleware that transforms HTTP request query strings into MongoDB-style query objects. Designed for RESTful APIs, it automatically parses pagination, sorting, and advanced filtering operators from query parameters and attaches the resulting object as req.parsedQuery.

## Features

- **Easy filtering:** Convert query strings like `age_gte=21` into MongoDB queries.
- **Sorting:** Multi-field sorting via `_sort` and `_order`.
- **Pagination:** Supports `_skip`, `_limit`, `_start`, `_end`, `_page`, `_per_page`.
- **Type casting:** Automatically parses numbers and booleans.
- **Advanced operators:** Supports Mongo-style operators: `_gt`, `_gte`, `_lt`, `_lte`, `_ne`.
- **Array support:** Handles repeated params as `$in` queries.

## Installation

Install via npm:

```sh
npm install @luikingkit/req2mongo
```

Or via yarn:

```sh
yarn add @luikingkit/req2mongo
```

## Usage

### 1. Attach middleware to your Express app

```js
const express = require('express');
const { parseMongoQuery } = require('@luikingkit/req2mongo');

const app = express();

// Apply the middleware
app.use(parseMongoQuery());

app.get('/users', (req, res) => {
  // Destructure results from req.parsedQuery
  const { filter, sort, skip, limit } = req.parsedQuery;
  // Use with Mongoose, MongoDB native, etc
  User.find(filter)
    .sort(sort)
    .skip(skip)
    .limit(limit)
    .then((users) => res.json(users));
});
```

### 2. Query parameter examples

| Query String                                                 | MongoDB Query Parsed Output                      |
| ------------------------------------------------------------ | ------------------------------------------------ |
| /users?\_sort=name&\_order=asc                               | sort: { name: 1 }                                |
| /users?\_sort=name&\_sort=createdAt&\_order=asc&\_order=desc | sort: { name: 1, createdAt: -1 }                 |
| /users?\_skip=10&\_limit=20                                  | skip: 10, limit: 20                              |
| /users?\_start=10&\_end=25                                   | skip: 10, limit: 15                              |
| /users?\_page=3&\_per_page=10                                | skip: 20, limit: 10                              |
| /users?status=active                                         | filter: { status: "active" }                     |
| /users?status=active&status=pending                          | filter: { status: { $in: ["active","pending"] }} |
| /users?age_gte=21                                            | filter: { age: { $gte: 21 } }                    |
| /users?age_gte=21&age_lte=30                                 | filter: { age: { $gte: 21, $lte: 30 } }          |

## Supported Query Operators

Use `field_operator=value`

Operators:

- no operator -> equal
- `lt` less than, `lte` less than or equal
- `gt` greater than, `gte` greater than or equal
- `ne` not equal

| Operator Example       | Parsed as                              | Matches MongoDB Operator |
| ---------------------- | -------------------------------------- | ------------------------ |
| `age=18`               | `{ age: 18 }`                          | `$eq` (default equality) |
| `score_gt=10`          | `{ score: { $gt: 10 } }`               | `$gt`                    |
| `score_gte=10`         | `{ score: { $gte: 10 } }`              | `$gte`                   |
| `qty_lt=20`            | `{ qty: { $lt: 20 } }`                 | `$lt`                    |
| `qty_lte=20`           | `{ qty: { $lte: 20 } }`                | `$lte`                   |
| `foo_ne=bar`           | `{ foo: { $ne: "bar" } }`              | `$ne`                    |
| `role=admin&role=user` | `{ role: { $in: ["admin", "user"] } }` | `$in`                    |

---

**Sorting:**

- Use `_sort=field1&_order=asc` or multiple:  
  `_sort=field1&_sort=field2&_order=asc&_order=desc`

**Pagination:**

- Supports `_skip`, `_limit`, `_start`, `_end`, `_page`, `_per_page`  
  Order of precedence: `_skip` & `_limit`, then `_start` & `_end`, then `_page` & `_per_page`

## Example

```js
// Request: GET /users?_sort=name&_sort=createdAt&_order=asc&_order=desc&age_gte=20&age_lte=30&role=admin&role=user&status=active&_skip=10&_limit=5

// req.parsedQuery will be:
{
  filter: {
    age: {
      $gte: 20,
      $lte: 30,
    },
    role: {
      $in: ['admin', 'user'],
    },
    status: 'active',
  },
  sort: {
    name: 1,
    createdAt: -1,
  },
  skip: 10,
  limit: 5,
}
```

## Type Casting

- `"123"` -> `123`
- `"true"` -> `true`
- `"false"` -> `false`
- All others remain strings

Array values such as `?flag=true&flag=false` -> `{ flag: { $in: [true, false] } }`

## Advanced

- No changes are made to existing properties on `req`.
- Safe to use with other Express middlewares.

## Contributing

1. Fork and clone the repository.
2. Make your changes in a branch.
3. Write tests for your changes.
4. Run npm test to ensure test suite passes.
5. Open a pull request.

## License

MIT © [Lui King Kit](https://github.com/luikingkit)

## Support

- Issues: [GitHub Issues](https://github.com/luikingkit/req2mongo/issues)
- Documentation: [Read the full docs](https://github.com/luikingkit/req2mongo#readme)