

# VantaApi: Advanced API Features & Security Config for MongoDB

This repository provides a robust, feature-rich, and secure solution for building, customizing, and optimizing your Node.js APIs powered by MongoDB. **VantaApi** processes incoming query parameters and builds an aggregation pipeline step by step, offering powerful features such as advanced filtering, sorting, field selection, pagination, and document population with comprehensive security controls.

---

## Table of Contents

1. [Installation & Setup](#installation--setup)
2. [Overview](#overview)
3. [ApiFeatures Class Methods](#ApiFeatures-class-methods)
   - [filter()](#filter)
   - [sort()](#sort)
   - [limitFields()](#limitfields)
   - [paginate()](#paginate)
   - [populate()](#populate)
   - [addManualFilters()](#addmanualfilters)
   - [execute()](#execute)
4. [Input Types & Supported Operators](#input-types--supported-operators)
5. [Additional Conditions](#additional-conditions)
6. [Security Configuration](#security-configuration)
7. [Security & Performance Enhancements](#security--performance-enhancements)
8. [Error Handling Middleware](#error-handling-middleware)
9. [Full Examples](#full-examples)
10. [Summary](#summary)

---

## Installation & Setup

### Prerequisites

- Node.js 16+
- MongoDB 5+
- Mongoose 7+

### Install Dependencies

Install core dependencies:

```bash
npm install mongoose lodash dotenv winston
```

For testing purposes, install Jest:

```bash
npm install --save-dev jest
```

---

## Overview

The **ApiFeatures** class processes incoming query parameters and progressively builds an aggregation pipeline. The package supports:

- **Advanced filtering, sorting, and field selection.**
- **Pagination with defaults** (defaults to page 1 and limit 10 if not provided), with the maximum limit based on the user's role.
- **Document population:** Supports joining related documents, including nested population.
- **Automatic Conditions:** If the model includes an `isActive` field and the user is not an admin, `isActive: true` is added automatically.
- **Input Sanitization & Validation:** Sanitizes inputs, validates numeric fields, and enforces security through a dedicated configuration.
- **Enhanced Logging & Error Handling:** Uses winston for logging and employs a custom error class for centralized error management.
- **Performance Improvements:** Features aggregation cursor support and optimized pipeline ordering.
- **Error Middleware:** Provides a centralized error handling middleware for consistent API error responses.

---

## ApiFeatures Class Methods

### filter()
- **Description:**  
  Parses query parameters, merges them with manually added filters (if provided), and applies security filters. If the model includes an `isActive` field and the user is not "admin," it automatically adds `isActive: true`.
- **Usage Example:**

  ```javascript
  // URL: /api/products?status=active&price[gte]=100
  const features = new ApiFeatures(Product, req.query);
  features.filter();
  // Pipeline adds: { $match: { status: "active", price: { $gte: 100 }, isActive: true } }
  ```

### sort()
- **Description:**  
  Converts a comma-separated list of sorting fields into a `$sort` object. A "-" prefix indicates descending order.
- **Usage Example:**

  ```javascript
  // URL: /api/products?sort=-price,createdAt
  const features = new ApiFeatures(Product, req.query);
  features.sort();
  // Pipeline adds: { $sort: { price: -1, createdAt: 1 } }
  ```

### limitFields()
- **Description:**  
  Uses `$project` to return only the specified fields while excluding forbidden fields (such as "password").
- **Usage Example:**

  ```javascript
  // URL: /api/products?fields=name,price,category,password
  const features = new ApiFeatures(Product, req.query);
  features.limitFields();
  // Pipeline adds: { $project: { name: 1, price: 1, category: 1 } }
  ```

### paginate()
- **Description:**  
  Determines the page and limit values, applying pagination with defaults (page 1 and limit 10 if not provided). The maximum limit is based on the user's role as defined in the security configuration.
- **Usage Example:**

  ```javascript
  // URL: /api/products?page=2&limit=20
  const features = new ApiFeatures(Product, req.query, "user");
  features.paginate();
  // Pipeline adds: { $skip: 20 } and { $limit: 20 }
  ```

### populate()
- **Description:**  
  Joins related documents using `$lookup` and `$unwind`. It supports:
  - **String Input:** A comma-separated list of field names.
  - **Object Input:** An object with properties `path` (required) and `select` (optional).
  - **Array Input:** An array of strings or objects for multiple or nested population.
- **Usage Examples:**
  - **String Input:**

    ```javascript
    // URL: /api/products?populate=category,brand
    const features = new ApiFeatures(Product, req.query);
    features.populate();
    ```

  - **Object Input:**

    ```javascript
    const populateOptions = { path: "category", select: "name description" };
    const features = new ApiFeatures(Product, req.query);
    features.populate(populateOptions);
    ```

  - **Array Input:**

    ```javascript
    const populateArray = [
      "brand",
      { path: "category", select: "name description" },
      { path: "category", select: "name", populate: { path: "subCategory", select: "title" } }
    ];
    const features = new ApiFeatures(Product, req.query, "admin");
    features.populate(populateArray);
    ```

### addManualFilters()
- **Description:**  
  Merges additional manual filters with the parsed query filters. **Note:** Call `addManualFilters()` before `filter()` to incorporate the manual filters properly.
- **Usage Example:**

  ```javascript
  const manualFilter = { category: "electronics" };
  const features = new ApiFeatures(Product, { status: "active" });
  features.addManualFilters(manualFilter).filter();
  ```

### execute()
- **Description:**  
  Executes the built aggregation pipeline using Mongoose and returns an object containing a success flag, total document count, and result data. Supports aggregation cursor for handling large datasets.
- **Usage Example:**

  ```javascript
  const features = new ApiFeatures(Product, req.query);
  const result = await features
    .filter()
    .sort()
    .limitFields()
    .paginate()
    .populate()
    .execute();
  console.log(result);
  ```

---

## Input Types & Supported Operators

### Filtering Operators

The class translates query parameters into MongoDB operators:

| Operator | Query Example             | Description                |
|----------|---------------------------|----------------------------|
| eq       | `?age=25`                 | Equal to                   |
| ne       | `?status[ne]=inactive`    | Not equal to               |
| gt       | `?price[gt]=100`          | Greater than               |
| gte      | `?stock[gte]=50`          | Greater than or equal to   |
| lt       | `?weight[lt]=500`         | Less than                  |
| lte      | `?rating[lte]=3`          | Less than or equal to      |
| in       | `?colors[in]=red,blue`    | In the list                |
| nin      | `?size[nin]=xl`           | Not in the list            |
| regex    | `?name[regex]=^A`         | Regex search               |
| exists   | `?discount[exists]=true`  | Field existence            |

Additional aspects like sorting, projection, and pagination are handled via `$sort`, `$project`, `$skip`, and `$limit`.

---

## Additional Conditions

- **isActive Condition:**  
  If the model includes an `isActive` field and the user is not an admin, `filter()` automatically adds `isActive: true`.
- **Default Pagination:**  
  Defaults are applied (page 1 and limit 10) when no pagination parameters are provided.
- **Numeric Validation:**  
  Validates that fields such as `page` and `limit` contain only numeric values.
- **Removal of Dangerous Operators:**  
  Operators such as `$where`, `$accumulator`, and `$function` are removed from the query.
- **Ordering of Manual Filters:**  
  Ensure that `addManualFilters()` is called before `filter()` so that manual filters are merged correctly.

---

## Security Configuration

Security settings enforce allowed operators, exclude forbidden fields (e.g., "password"), and apply role-based access limits:

```javascript
export const securityConfig = {
  allowedOperators: [
    "eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "regex", "exists", "size", "or", "and"
  ],
  forbiddenFields: ["password"],
  accessLevels: {
    guest: { maxLimit: 50, allowedPopulate: ["*"] },
    user: { maxLimit: 100, allowedPopulate: ["*"] },
    admin: { maxLimit: 1000, allowedPopulate: ["*"] },
    superAdmin: { maxLimit: 1000, allowedPopulate: ["*"] }
  }
};
```

These configurations are applied automatically in methods such as `filter()`, `paginate()`, and `limitFields()`.

---

## Security & Performance Enhancements

- **Enhanced Logging:**  
  Uses winston to log events and errors with detailed timestamps and stack traces.
- **Improved Error Handling:**  
  A custom error class (HandleERROR) coupled with dedicated error middleware centralizes error management.
- **Dynamic Configuration:**  
  Security settings are maintained in a separate config file (`config.js`) for easy modifications without changing core code.
- **Performance Optimization:**  
  Features aggregation cursor support in `execute()` and optimized pipeline ordering for resource-efficient execution.

---

## Error Handling Middleware

Vanta-API provides a centralized error handling system featuring three components:

### 1. handleError

**Purpose:**  
A custom error class that extends the native JavaScript `Error`.  
It adds:
- **`statusCode`:** HTTP status code.
- **`status`:** Determines if the error is a `"fail"` (client error) or `"error"` (server error).
- **`isOperational`:** Flags if the error is an expected operational error.
- **Stack Trace:** Captures the call stack for easier debugging.

**Usage Example in an Async Function:**

Inside an asynchronous function wrapped by **catchAsync**, you can use:

```javascript
// Inside your async route handler
if (someConditionFails) {
  return next(new handleError("Custom error message", 400));
}
```

### 2. catchAsync

**Purpose:**  
A helper function that wraps asynchronous route handlers.  
It automatically catches any thrown errors and forwards them via `next()`, avoiding repetitive try/catch blocks.

**Usage Example:**

```javascript
app.get("/example", catchAsync(async (req, res, next) => {
  // Your async logic here
  // If an error occurs, use handleError as shown above
  res.status(200).json({ data: "Success" });
}));
```

### 3. catchError

**Purpose:**  
An Express middleware that catches any error passed along (either from synchronous or asynchronous routes).  
It sets the appropriate HTTP status and returns a JSON response containing the error’s status and message.

**Usage Example:**

```javascript
// At the end of your middleware stack:
app.use(catchError);
```

---


---

## Full Examples

### Example 1: Basic Query
To import the package along with the error handling components, use:

```javascript
import ApiFeatures, { handleError, catchAsync, catchError } from "vanta-api";
```

```javascript
import Product from "./models/product.js";

// URL: /api/products?status=active&price[gte]=100&sort=-price,createdAt&fields=name,price,category&page=1&limit=10&populate=category,brand
const features = new ApiFeatures(Product, req.query, "user");
const result = await features
  .filter()
  .sort()
  .limitFields()
  .paginate()
  .populate()
  .execute();
console.log(result);
```

### Example 2: Query with Manual Filters

*(Call `addManualFilters()` before `filter()`)*

```javascript
const query = { status: "active" };
const manualFilter = { category: "electronics" };
const features = new ApiFeatures(Product, query, "user");
features.addManualFilters(manualFilter).filter();
const result = await features.execute();
console.log(result);
```

### Example 3: Advanced Nested Populate with Array Input

```javascript
const populateArray = [
  "brand", // Simple string input
  { path: "category", select: "name description" },
  { path: "category", select: "name", populate: { path: "subCategory", select: "title" } }
];
const features = new ApiFeatures(Product, req.query, "admin");
const result = await features.populate(populateArray).execute();
console.log(result);
```

### Example 4: Full Advanced Query Example

```http
GET /api/products?
  page=1&
  limit=10&
  sort=-createdAt,price&
  fields=name,price,category&
  populate=category,brand&
  price[gte]=1000&
  category[in]=electronics,phones&
  name[regex]=^Samsung
```

### Example 5: Using Default Pagination (when page and limit are not provided)

```javascript
// URL: /api/products?status=active
const features = new ApiFeatures(Product, req.query);
const result = await features
  .filter() // Defaults to page 1 and limit 10
  .execute();
console.log(result);
```

---

## Summary

- **Filtering:**  
  Combines query parameters with manual filters and applies a safe `$match` with an automatic `isActive: true` condition for non-admin users.
- **Sorting:**  
  Converts a comma-separated string into a proper `$sort` object.
- **Field Selection:**  
  Uses `$project` to include only permitted fields while excluding forbidden fields.
- **Pagination:**  
  Applies `$skip` and `$limit` with default values (page 1, limit 10) and role-based limits.
- **Populate:**  
  Joins related documents using `$lookup` and `$unwind`, supporting nested and varied input types.
- **Security:**  
  Enforces allowed operators, sanitizes inputs, validates numeric fields, and removes dangerous operators via the security configuration.
- **Logging & Error Handling:**  
  Integrated advanced logging using winston and centralized error handling with a custom error class and middleware.
- **Performance Optimizations:**  
  Supports aggregation cursor for large datasets and optimizes aggregation pipelines for efficient resource usage.
  
- **ApiFeatures:**  
  Provides advanced query capabilities such as filtering, sorting, pagination, and document population for your MongoDB data.

- **Error Handling Components:**
  - **handleError:**  
    Throw consistent, structured errors with custom messages and status codes.
  - **catchAsync:**  
    Wrap asynchronous route handlers to automatically propagate errors.
  - **catchError:**  
    Centralized middleware to catch and respond to errors uniformly.

- **Importing:**  
  Use the following statement to access all features:

  ```javascript
  import ApiFeatures, { handleError, catchAsync, catchError } from "vanta-api";
  ```

By following these guidelines, you can integrate and use Vanta-API for advanced, secure query handling and robust error management in your Node.js/Express projects.

---

VantaApi provides a complete solution for integrating powerful, secure, and customizable query capabilities into any Node.js/MongoDB project.

---

