@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.
Maintainers
Readme
# CRUD ROUTER
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, andremovefor any Mongoose model. - Auto-Expose API: Automatically scan and expose CRUD endpoints from your models directory using
craft.ymlorcraft.jsonconfiguration. - Bulk Action:
bulkUpdate,bulkDelete,bulkCreateactions 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, andafterDelete. - 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
limitandpagequery parameters for paginated results. - Custom middleware: Apply middleware functions selectively per CRUD route (e.g., middleware only for
getAllordelete). - Populate support: Use
?populate=user,categoryto populate references on any route. - Projection support: Use
?fields=name,priceto return only specific fields, with the ability to hide sensitive fields by default.
Installation
npm install @api-craft/crud-routeror with pnpm:
pnpm add @api-craft/crud-routerUsage
💡 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-yaml2. 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 skipfilesIgnore: Array of filenames to skip during importroutes.[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 withgetAllandgetOnearrays 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: ProductNamed 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: TagMixed 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 reuseGrouped 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:Useruser.model.js,UserModel.js→ Model name:Useruser.schema.js,user-schema.js→ Model name:User- The suffix
.model,.schema,.entity,.collectionis 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').connectionConnection Resolution Priority:
opts.connection(direct connection object)opts.mongoose.connection(mongoose instance)app.locals.mongoose.connection(stored in app locals)app.get('mongoose').connection(stored in app settings)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.
