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

@api-craft/crud-router

v2.3.2

Published

A lightweight Express middleware to automatically generate CRUD routes and controllers from Mongoose models, simplifying API development.

Readme

# CRUD ROUTER

NPM Downloads NPM Downloads GitHub Repo stars GitHub Release GitHub contributors

A flexible and customizable CRUD router generator for Express.js and Mongoose, designed to speed up API development with powerful filtering, pagination, bulk actions, projection, populate, and middleware support.


Features

  • Automatic CRUD routes: getAll, getById, create, update, and remove for any Mongoose model.
  • Auto-Expose API: Automatically scan and expose CRUD endpoints from your models directory using craft.yml or craft.json configuration.
  • Bulk Action: bulkUpdate, bulkDelete, bulkCreate actions are supported.
  • Exclude routes: Easily exclude any CRUD method when generating the router.
  • Lifecycle hooks: Attach hooks for beforeGetAll, afterGetAll, beforeGetById, afterGetById, beforeCreate, afterCreate, beforeUpdate, afterUpdate, beforeDelete, and afterDelete.
  • Query parsing: Support MongoDB-style filtering via URL query parameters with operators like:
    • gt (greater than)
    • gte (greater than or equal)
    • lt (less than)
    • lte (less than or equal)
    • ne (not equal)
    • in (array inclusion)
  • Pagination: Use limit and page query parameters for paginated results.
  • Custom middleware: Apply middleware functions selectively per CRUD route (e.g., middleware only for getAll or delete).
  • Populate support: Use ?populate=user,category to populate references on any route.
  • Projection support: Use ?fields=name,price to return only specific fields, with the ability to hide sensitive fields by default.

Installation

npm install @api-craft/crud-router

or with pnpm:

pnpm add @api-craft/crud-router

Usage

💡 Important: When using autoExposeFromCraft, always pass your mongoose instance (see Using Your Mongoose Instance) to avoid version conflicts.

Manual Router Setup

import express from 'express';
import mongoose from 'mongoose';
import { createCrud } from '@api-craft/crud-router';

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

const productSchema = new mongoose.Schema({
  name: String,
  price: Number,
  category: { type: mongoose.Schema.Types.ObjectId, ref: 'Category' },
});

const Product = mongoose.model('Product', productSchema);

const productRouter = createCrud(Product, {
  excluded: ['remove'], // Exclude single delete if needed
  hooks: {
    beforeCreate: async (data) => {
      // Modify data before creation
      return data;
    },
  },
  middlewares: {
    getAll: [(req, res, next) => {
      console.log('Middleware for getAll route');
      next();
    }],
  },
  hide: {
    getAll: ['internalCode'], // Example: Hide sensitive field from responses
  },
});

app.use('/api/products', productRouter);

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Auto-Expose API from Models Directory

Automatically generate and mount CRUD routes for all models in a directory using configuration:

1. Install js-yaml (if using YAML config):

pnpm add js-yaml

2. Create craft.yml or craft.json in your project root:

# craft.yml
basePath: /api
modelsDir: ./models

ignore:
  - InternalAudit

filesIgnore:
  - index.js
  - README.md

routes:
  Product:
    path: /products
    excluded: [remove]
    hide:
      getAll: [internalCode]
      getOne: [internalCode]
  User:
    path: /users
    excluded: [create, remove]

Or using JSON:

{
  "basePath": "/api",
  "modelsDir": "./models",
  "ignore": ["InternalAudit"],
  "filesIgnore": ["index.js"],
  "routes": {
    "Product": {
      "path": "/products",
      "excluded": ["remove"],
      "hide": {
        "getAll": ["internalCode"]
      }
    },
    "User": {
      "path": "/users",
      "excluded": ["create", "remove"]
    }
  }
}

3. Use autoExposeFromCraft in your server:

import express from 'express';
import mongoose from 'mongoose';
import { autoExposeFromCraft } from '@api-craft/crud-router';

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

await mongoose.connect(process.env.MONGO_URI);

// RECOMMENDED: Pass your mongoose instance to avoid version conflicts
await autoExposeFromCraft(app, { mongoose });

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

💡 Best Practice: Always pass your mongoose instance via { mongoose } or { connection } to avoid conflicts between different mongoose versions in your node_modules tree.

Configuration Options:

  • basePath: Base path for all routes (default: /api)
  • modelsDir: Directory containing model files (default: ./models)
  • ignore: Array of model names to skip
  • filesIgnore: Array of filenames to skip during import
  • routes.[ModelName].path: Custom path for model (default: kebab-case pluralized model name)
  • routes.[ModelName].excluded: Array of methods to exclude (getAll, getById, create, update, remove, updateBulk, removeBulk)
  • routes.[ModelName].hide: Object with getAll and getOne arrays of fields to hide

Supported Model Export Patterns

The auto-expose feature intelligently handles various export patterns:

Default Export - Model:

// User.js or user.model.js
import mongoose from 'mongoose';
const schema = new mongoose.Schema({ name: String });
export default mongoose.model('User', schema);

Default Export - Schema:

// Product.js or product.model.js
import mongoose from 'mongoose';
export default new mongoose.Schema({ name: String, price: Number });
// Model name inferred from filename: Product

Named Export - Model:

// category.model.js
import mongoose from 'mongoose';
const schema = new mongoose.Schema({ title: String });
export const Category = mongoose.model('Category', schema);

Named Export - Schema:

// tag.model.js
import mongoose from 'mongoose';
export const TagSchema = new mongoose.Schema({ label: String });
// Model name inferred from filename: Tag

Mixed Exports:

// Order.js
import mongoose from 'mongoose';
const OrderSchema = new mongoose.Schema({ total: Number });
const Order = mongoose.model('Order', OrderSchema);
export default Order;
export { OrderSchema }; // Also exported for reuse

Grouped Exports:

// index.js
import User from './User.js';
import Product from './Product.js';
export const models = { User, Product };

Factory Functions:

// Multi-tenant pattern
export default (connection) => {
  const schema = new connection.Schema({ name: String });
  return connection.model('Tenant', schema);
};

File Naming Conventions:

  • User.js, user.js → Model name: User
  • user.model.js, UserModel.js → Model name: User
  • user.schema.js, user-schema.js → Model name: User
  • The suffix .model, .schema, .entity, .collection is automatically removed

Using Your Mongoose Instance (Recommended)

Why pass your mongoose instance?

  • ✅ Avoids mongoose version conflicts between your app and node_modules
  • ✅ Ensures models are registered on the same connection instance
  • ✅ Prevents "Schema hasn't been registered" errors
  • ✅ Required for multi-tenant or multi-database setups
import mongoose from 'mongoose';
import { autoExposeFromCraft } from '@api-craft/crud-router';

// Method 1: Pass mongoose instance (RECOMMENDED)
await mongoose.connect(process.env.MONGO_URI);
await autoExposeFromCraft(app, { mongoose });

// Method 2: Pass specific connection (multi-tenant setups)
const tenantConn = await mongoose.createConnection(process.env.TENANT_DB_URI);
await autoExposeFromCraft(app, { 
  connection: tenantConn,
  mountBase: '/api/tenant'
});

// Method 3: Store in app locals (alternative)
app.locals.mongoose = mongoose;
await autoExposeFromCraft(app); // Will use app.locals.mongoose.connection

// Method 4: Using app.set (Express pattern)
app.set('mongoose', mongoose);
await autoExposeFromCraft(app); // Will use app.get('mongoose').connection

Connection Resolution Priority:

  1. opts.connection (direct connection object)
  2. opts.mongoose.connection (mongoose instance)
  3. app.locals.mongoose.connection (stored in app locals)
  4. app.get('mongoose').connection (stored in app settings)
  5. mongoose.connection (global fallback - not recommended)

API Response Format

All getAll endpoints return data in the following format:

{
  "data": [...],
  "meta": {
    "total": 100,
    "limit": 10,
    "currentPage": 2,
    "totalPages": 10,
    "hasNextPage": true,
    "hasPrevPage": true
  }
}

When pagination parameters (limit and page) are not provided, meta.pagination will be false and only total is included.


Example Query Features

  • Pagination: /api/products?limit=10&page=2
  • Filtering: /api/products?price=gt-100
  • Field Selection (Projection): /api/products?fields=name,price
  • Populate Related Data: /api/products?populate=category

Author

Developed and Maintained by @tselven

Contributors


Contributions

Contributions are welcome for:

  • Impliment new drivers and extend the functionalities
  • Create and Maintain Docs Pages
  • Bug Fix and Code Quality Maintanence.