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 πŸ™

Β© 2025 – Pkg Stats / Ryan Hefner

maggie-api

v1.1.3

Published

πŸ§™β€β™€οΈ A magical Express middleware to auto-generate CRUD APIs for Mongoose models with validation, unique keys, and middlewares.

Readme

πŸ§™β€β™€οΈ maggie-api

Auto-generate full-featured CRUD APIs for your Mongoose models in Express with one powerful config.

Supports:

  • βœ… Joi Validation
  • βœ… Custom Middlewares
  • βœ… Unique Primary Key Constraints
  • βœ… Add/Update Merged API
  • βœ… Consistent JSON Responses
  • βœ… Field Selection & Population
  • βœ… Bulk Insert Support
  • βœ… Dynamic Search (with keyword, fields, and case sensitivity support)
  • βœ… Pagination Support (limit & page query)
  • βœ… Sorting Support (ascending, descending, multi-field)
  • βœ… Filtering Support (with allowed fields and advanced operators like $gte, $in)
  • βœ… Auto Pluralized Response Keys
  • βœ… Full CRUD API Auto-generation

πŸ“¦ Installation

npm install maggie-api

# Peer dependencies
npm install express mongoose joi

πŸš€ Quick Start

import express from "express";
import { createMaggie } from "maggie-api";
import Models from "./models";
import Joi from "joi";

const app = express();
app.use(express.json());

const UserValidationSchema = Joi.object({
  _id: Joi.string(),
  firstName: Joi.string().required(),
  lastName: Joi.string().required(),
  email: Joi.string().email().required(),
});

const apiRouter = createMaggie({
  prefix: "/api/v1",
  models: [
    {
      model: ProductModel,
      path: "product",
      validationSchema: productValidationSchema,
      settings: {
        get: {
          // βœ… Only these fields will be returned in GET /product
          keys: ["_id", "title", "price", "description", "subCategory"],

          // πŸ” Search by title or description using `?search=some+word`
          search: {
            disabled: false,
            allowedFields: ["title", "description"],
          },

          // 🧹 Allow filtering via `?filter[price][gte]=100` or `filter[title]=Shoes`
          filter: {
            allowedFields: ["price", "title", "subCategory"],
          },

          // πŸ”— Populate referenced subCategory and its category
          populate: [
            {
              path: "subCategory",
              select: ["_id", "title"],
              populate: [{ path: "category", select: ["_id", "title"] }],
            },
          ],
        },

        getById: {
          // βœ… Only these fields will be returned in GET /product/:id
          keys: ["_id", "title", "description", "price", "subCategory"],

          // πŸ”— Nested populate same as `get`
          populate: [
            {
              path: "subCategory",
              select: ["_id", "title"],
              populate: [{ path: "category", select: ["_id", "title"] }],
            },
          ],
        },
      },
    },
  ],
});

app.use(apiRouter);

app.listen(3000, () => {
  console.log("Server running at http://localhost:3000");
});

πŸ›  Features

1. Add or Update API (POST /:model)

  • Merges create and update logic into a single endpoint.
  • If the request body contains _id, it triggers an update; otherwise, a new record is created.
  • Automatically checks primaryKey uniqueness during creation.
  • During update, it ignores the current document when checking for duplicates.

2. Joi Validation

  • Supports request body validation using Joi schemas for POST operations.
  • Only one validation error message is returned per request to enhance clarity.
  • Validation schemas are customizable per model.

3. Primary Key Uniqueness

  • Define a primaryKey (e.g. email, username) to enforce uniqueness on creation.
  • If a duplicate is found, the API returns a descriptive error.

4. Custom Middlewares

  • Use the middleWares array to inject custom Express middlewares into the POST route.
  • Enables features like authentication, authorization, logging, etc.

5. Field Filtering (Deprecated)

  • ⚠️ getKeys and getByIdKeys are deprecated.
  • Use settings.get.keys to select fields in GET /:model.
  • Use settings.getById.keys to select fields in GET /:model/:id.
  • This improves flexibility and aligns with modern structured configurations.

6. CRUD Endpoints (Auto-generated)

| Method | Endpoint | Description | | -------- | ------------------- | --------------------- | | POST | /api/v1/user | Create or Update User | | POST | /api/v1/user/bulk | Bulk Insert Users | | GET | /api/v1/user | Fetch all Users | | GET | /api/v1/user/:id | Fetch User by ID | | DELETE | /api/v1/user/:id | Delete User by ID |


7. Population Support

  • Use settings.get.populate and settings.getById.populate to populate referenced fields.
  • Each populate config accepts a path and optional select array for nested or targeted population.
settings: {
  get: {
    populate: [
      { path: "department", select: ["_id", "title"] }
    ]
  },
  getById: {
    populate: [
      {
        path: "department",
        select: ["_id", "title"] ,
        populate: [
          {
            path: "item",
            selected: ["_id", "title"]
          }
         ],
      }
    ]
  }
}

8. Search Support

  • βœ… Use settings.get.search to enable keyword-based searching on specific fields.
  • πŸ” Accepts query parameters like search, searchFields, and caseSensitive.
  • 🧩 Only fields defined in allowedFields will be considered for searching.
  • πŸ›‘ If disabled: true, searching will be turned off for that model.
  • 🌐 Falls back to all allowed fields if searchFields param is not provided.

Example Setting:

settings: {
  get: {
    search: {
      disabled: false,
      allowedFields: ["title", "description", "email"]
    }
  }
}

Sample Request:

GET /api/v1/user?search=mascara&searchFields=title,description&caseSensitive=false

Behavior:

  • Builds a $or regex search query for all specified fields.
  • If no valid fields are provided or allowed β†’ search is skipped.

9. Sorting, Pagination & Filtering (Built-in)

Sorting, pagination, and filtering are first-class citizens in maggie-api, available out of the box for all models.

πŸ”€ Sorting

  • Pass a sort query param to define sort order:

    ?sort=-createdAt,name
  • Use a hyphen (-) prefix for descending order.

  • Multiple fields can be sorted in sequence.

  • Sorting is always enabled β€” no extra config needed.

πŸ“„ Pagination

  • Supports standard pagination via limit and page query parameters:

    ?limit=10&page=2
  • Only applied when both parameters are valid positive integers.

  • Automatically returns metadata:

    {
      "users": [...],
      "pagination": {
        "total": 100,
        "page": 2,
        "limit": 10,
        "totalPages": 10
      }
    }
  • If not provided, returns the full result set without pagination.

πŸ“Œ Example:

GET /api/v1/product?filter[price][gte]=500&sort=-createdAt&limit=10&page=1

⚠️ Sorting and pagination are always enabled by default. Filtering requires configuring allowedFields to avoid accidental or insecure filtering.

This makes it easy to power powerful, customizable tables and dashboards with minimal backend configuration.


10. Filter Support

maggie-api allows powerful and flexible filtering on API endpoints using structured query parameters.

πŸ”§ Key Features:

  • Declarative control over filterable fields via settings.get.filter.allowedFields
  • Automatically transforms nested filters into MongoDB-compatible queries
  • Supports value types: primitives, arrays, and range operators

πŸ”€ Supported Operators:

| Operator | Usage | Translates To | | -------- | ------------------------ | -------------------------- | | eq | filter[status]=active | { status: "active" } | | in | filter[tags][]=a&[]=b | { tags: { $in: [...] } } | | gte | filter[price][gte]=100 | { price: { $gte: 100 } } | | lte | filter[price][lte]=500 | { price: { $lte: 500 } } | | gt, lt | Similar usage | $gt, $lt |

πŸ’‘ Behavior:

  • If a filter field is not included in allowedFields, it will be silently ignored.
  • Case-sensitive by default (you may use search for regex-based keyword lookups).
  • Compatible with MongoDB query syntax for advanced filtering.

πŸ§ͺ Example Request:

GET /api/v1/user?filter[role]=admin&filter[age][gte]=18

⚠️ Important:

  • Always whitelist filterable fields to avoid misuse or performance hits
  • For flexible keyword matching across multiple fields, use the search config instead

This filtering system is perfect for admin dashboards, search filters, and dynamic list views.


πŸ“‘ Sample cURL Commands

βž• Add a User

curl -X POST http://localhost:3000/api/v1/user \
-H "Content-Type: application/json" \
-d '{"firstName":"Alice","lastName":"Doe","email":"[email protected]"}'

✏️ Update a User

curl -X POST http://localhost:3000/api/v1/user \
-H "Content-Type: application/json" \
-d '{"_id":"665c8d1234567890","firstName":"Alicia","email":"[email protected]"}'

πŸ“„ Get All Users

curl http://localhost:3000/api/v1/user

πŸ” Get User by ID

curl http://localhost:3000/api/v1/user/665c8d1234567890

❌ Delete User by ID

curl -X DELETE http://localhost:3000/api/v1/user/665c8d1234567890

🚚 Bulk Insert Users

curl -X POST http://localhost:3000/api/v1/user/bulk \
-H "Content-Type: application/json" \
-d '[
  {"firstName":"Bob","lastName":"Smith","email":"[email protected]"},
  {"firstName":"Carol","lastName":"Jones","email":"[email protected]"}
]'

βœ… Standard JSON Response Format

maggie-api follows a consistent, frontend-friendly response structure for all CRUD operations.

🟒 On Success

Create:

{
  "success": true,
  "statusCode": 201,
  "message": "User created successfully",
  "data": {
    "_id": "665c8d1234567890",
    "firstName": "Alice",
    "email": "[email protected]"
  }
}

Update:

{
  "success": true,
  "statusCode": 200,
  "message": "User updated successfully",
  "data": {
    "_id": "665c8d1234567890",
    "firstName": "Alicia"
  }
}

Get All (with optional pagination):

{
  "success": true,
  "statusCode": 200,
  "message": "Users fetched successfully",
  "data": {
    "users": [...],
    "pagination": {
      "total": 100,
      "page": 2,
      "limit": 10,
      "totalPages": 10
    }
  }
}

Get by ID:

{
  "success": true,
  "statusCode": 200,
  "message": "User fetched successfully",
  "data": {
    "_id": "665c8d1234567890",
    "firstName": "Alice"
  }
}

Delete:

{
  "success": true,
  "statusCode": 200,
  "message": "User deleted successfully",
  "data": {
    "_id": "665c8d1234567890"
  }
}

Bulk Insert:

{
  "success": true,
  "statusCode": 201,
  "message": "3 Users created successfully",
  "data": [{}, {}, {}]
}

πŸ”΄ On Errors

Validation Error:

{
  "success": false,
  "statusCode": 400,
  "message": "Validation error",
  "error": "\"email\" is required"
}

Duplicate Primary Key (Create or Bulk Insert):

{
  "success": false,
  "statusCode": 409,
  "message": "User with this email already exists",
  "data": null
}

Document Not Found (Update or GetById):

{
  "success": false,
  "statusCode": 404,
  "message": "User not found",
  "data": null
}

Invalid Request Format (e.g. Bulk Insert with non-array):

{
  "success": false,
  "statusCode": 400,
  "message": "Request body must be a non-empty array of documents",
  "data": null
}

Server Error:

{
  "success": false,
  "statusCode": 500,
  "message": "Failed to process User",
  "data": null
}

πŸ“‚ Example Project Structure

your-app/
β”œβ”€β”€ models/
β”‚   └── User.ts
β”œβ”€β”€ routes/
β”‚   └── index.ts
β”œβ”€β”€ utils/
β”‚   └── validateBody.ts
β”œβ”€β”€ app.ts
└── ...

πŸ‘ Contributing

Want to contribute or enhance? PRs are welcome!

  • Add new features like PATCH support, role-based auth, etc.
  • Improve test coverage
  • Bug fixes

πŸ“’ Final Words

Save hours of boilerplate setup. Focus on your app logic.

Let maggie-api handle the API plumbing. πŸš€