One model in,a whole REST API out.
controller-sets turns a Mongoose model into six working Express endpoints — list, query, read, create, update, delete — with filtering, search, pagination, caching and file uploads already wired up. You write the model and say what clients are allowed to do; it writes the controller.
What you write
createRouter({
model: Product,
query: ['category'],
search: ['name'],
allowedFields: ['name', 'price'],
});
What you get
- GET
/productsfilter · search · page - QUERY
/productsthe same read, from a JSON body - POST
/productscreate - GET
/products/:idread one - PATCH
/products/:idupdate - DELETE
/products/:iddelete
What it does for you#
Every Express + Mongoose API writes the same controllers: list with filters, read one, create, update, delete — and each hand-written copy has its own bugs. controller-sets writes them once and lets you configure the rest.
Less code
One call gives a model six endpoints with filtering, search, sorting and pagination. No controller code to write or test.
Secure by default
Nothing is filterable or sortable until you allow it. Mass assignment, NoSQL injection and regex attacks are closed off.
Fast at any size
Cursor pagination serves page 1,000 of 200,000 records in under a millisecond; opt-in Redis caching skips the database entirely.
Batteries included
S3 uploads with image compression, JWT auth with refresh tokens and social sign-in, email and SMS codes — each optional.
Your models
No schema of its own. Point it at the Mongoose models you already have.
Typed
Full TypeScript declarations ship with the package — autocomplete for every option.
Which page do I need?#
The docs are split by what you are trying to do. Guides explain how and why; Reference pages are look-up tables.
| I want to… | Go to |
|---|---|
| Get something running for the first time | Quickstart |
| Understand the mental model before configuring anything | Core concepts |
| Decide what clients can filter, search, sort and write | createRouter |
| Require login, split public and admin access, or show users only their own data | Protecting routes |
| Accept images or files | File uploads |
| Make reads faster with Redis | Caching |
| Add sign-up, login, roles and password reset | Authentication and Email & SMS |
| Mix generated handlers with my own routes | Custom routes |
| Look up any option, its default and why to use it | createRouter |
| Call the API from a web or mobile client | HTTP API |
| Fix an error or unexpected response | Troubleshooting |
Less code, same result#
app.use('/api/products', createRouter({
model: Product,
query: ['category'],
search: ['name'],
sortableFields: ['price', 'createdAt'],
allowedFields: ['name', 'price', 'category'],
}));
Six endpoints, filtered input, pagination, consistent errors.
router.get('/', async (req, res, next) => {
try {
const filter = {};
if (req.query.category) filter.category = req.query.category;
if (req.query.s) filter.name = { $regex: escape(req.query.s), $options: 'i' };
const sort = ['price', 'createdAt'].includes(req.query.sort?.replace('-', ''))
? req.query.sort : '-createdAt';
const page = Math.max(1, parseInt(req.query.page) || 1);
const limit = Math.min(100, parseInt(req.query.pageSize) || 50);
const [data, total] = await Promise.all([
Product.find(filter).sort(sort).skip((page - 1) * limit).limit(limit),
Product.countDocuments(filter),
]);
res.json({ success: true, data, pagination: { page, total } });
} catch (err) { next(err); }
});
// …and POST, GET /:id, PATCH /:id, DELETE /:id, input filtering, id validation…
Installation#
npm install express-controller-sets express mongoose
| Requirement | Version |
|---|---|
| Node.js | 20.19+ — 22.2+ for the QUERY route (skipped with one warning on older Node) |
| Express | 5 |
| Mongoose | 9 |
| Module system | ES modules — "type": "module" in package.json. From CommonJS, use await import(). |
Everything else is optional. Install a package only when you use the feature — they load lazily, so an app that never uploads never loads the AWS SDK.
| Feature | Extra install |
|---|---|
| CRUD routes, authentication | nothing |
| File uploads | npm install multer @aws-sdk/client-s3 |
| Image compression | npm install sharp |
| Redis cache | npm install ioredis (or redis) |
| Email via SMTP | npm install nodemailer |
Next: the Quickstart builds a working API step by step.