UNPKG

maggie-api

Version:

๐Ÿง™โ€โ™€๏ธ A magical Express middleware to auto-generate CRUD APIs for Mongoose models with validation, unique keys, and middlewares.

548 lines (416 loc) โ€ข 12.4 kB
# ๐Ÿง™โ€โ™€๏ธ 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 ```bash npm install maggie-api # Peer dependencies npm install express mongoose joi ``` --- ## ๐Ÿš€ Quick Start ```ts 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. ```ts 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:** ```ts settings: { get: { search: { disabled: false, allowedFields: ["title", "description", "email"] } } } ``` **Sample Request:** ```http 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: ```http ?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: ```http ?limit=10&page=2 ``` - Only applied when **both** parameters are valid positive integers. - Automatically returns metadata: ```json { "users": [...], "pagination": { "total": 100, "page": 2, "limit": 10, "totalPages": 10 } } ``` - If not provided, returns the full result set without pagination. #### ๐Ÿ“Œ Example: ```http 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: ```http 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 ```bash curl -X POST http://localhost:3000/api/v1/user \ -H "Content-Type: application/json" \ -d '{"firstName":"Alice","lastName":"Doe","email":"alice@example.com"}' ``` ### โœ๏ธ Update a User ```bash curl -X POST http://localhost:3000/api/v1/user \ -H "Content-Type: application/json" \ -d '{"_id":"665c8d1234567890","firstName":"Alicia","email":"alice@example.com"}' ``` ### ๐Ÿ“„ Get All Users ```bash curl http://localhost:3000/api/v1/user ``` ### ๐Ÿ” Get User by ID ```bash curl http://localhost:3000/api/v1/user/665c8d1234567890 ``` ### โŒ Delete User by ID ```bash curl -X DELETE http://localhost:3000/api/v1/user/665c8d1234567890 ``` ### ๐Ÿšš Bulk Insert Users ```bash curl -X POST http://localhost:3000/api/v1/user/bulk \ -H "Content-Type: application/json" \ -d '[ {"firstName":"Bob","lastName":"Smith","email":"bob@example.com"}, {"firstName":"Carol","lastName":"Jones","email":"carol@example.com"} ]' ``` --- ## โœ… Standard JSON Response Format `maggie-api` follows a consistent, frontend-friendly response structure for all CRUD operations. ### ๐ŸŸข On Success **Create:** ```json { "success": true, "statusCode": 201, "message": "User created successfully", "data": { "_id": "665c8d1234567890", "firstName": "Alice", "email": "alice@example.com" } } ``` **Update:** ```json { "success": true, "statusCode": 200, "message": "User updated successfully", "data": { "_id": "665c8d1234567890", "firstName": "Alicia" } } ``` **Get All (with optional pagination):** ```json { "success": true, "statusCode": 200, "message": "Users fetched successfully", "data": { "users": [...], "pagination": { "total": 100, "page": 2, "limit": 10, "totalPages": 10 } } } ``` **Get by ID:** ```json { "success": true, "statusCode": 200, "message": "User fetched successfully", "data": { "_id": "665c8d1234567890", "firstName": "Alice" } } ``` **Delete:** ```json { "success": true, "statusCode": 200, "message": "User deleted successfully", "data": { "_id": "665c8d1234567890" } } ``` **Bulk Insert:** ```json { "success": true, "statusCode": 201, "message": "3 Users created successfully", "data": [{}, {}, {}] } ``` --- ### ๐Ÿ”ด On Errors **Validation Error:** ```json { "success": false, "statusCode": 400, "message": "Validation error", "error": "\"email\" is required" } ``` **Duplicate Primary Key (Create or Bulk Insert):** ```json { "success": false, "statusCode": 409, "message": "User with this email already exists", "data": null } ``` **Document Not Found (Update or GetById):** ```json { "success": false, "statusCode": 404, "message": "User not found", "data": null } ``` **Invalid Request Format (e.g. Bulk Insert with non-array):** ```json { "success": false, "statusCode": 400, "message": "Request body must be a non-empty array of documents", "data": null } ``` **Server Error:** ```json { "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. ๐Ÿš€