📄 mongodb-api-router
Documentation
Welcome to the comprehensive documentation for mongodb-api-router
, a powerful factory for creating multilingual, filterable, paginated CRUD API routes in an Express.js + Mongoose application.
📋 Table of Contents
1. Overview
mongodb-api-router
exports:
- A multilingual message system (
messages
, defineMessage
, message
).
- A
BrowserLanguage
Symbol for locale control.
- A default export:
apiRoute(model, options)
— a factory that generates Express middleware for RESTful endpoints on a given Mongoose model.
Features:
- Automatic GET, POST, PUT, DELETE handling.
- Filtering, pagination, field renaming and skimming (post‐query filtering).
- Custom middleware hooks per HTTP method.
- Error translation into multiple languages.
2. Exports
BrowserLanguage | Symbol | Force-use a specific language instead of the browser’s. |
defineMessage | Function | Add or override localized messages by code number. |
message | Function | Retrieve a translated message (with placeholders). |
apiRoute (default) | Function | Factory to create an Express.js route handler for a Mongoose model. |
3. Localization Messages
A set of default messages keyed by numeric codes (1–11), each with translations:
1 | The request is invalid. |
2 | You cannot filter results by the “{key}” parameter. |
3 | The field “{target}” is required. |
4 | The field “{target}” is too short. |
5 | The field “{target}” is too long. |
6 | The value of “{target}” is too low. |
7 | The value of “{target}” is too high. |
8 | The value of “{target}” is not valid. |
9 | The format of “{target}” is incorrect. |
10 | The value of “{target}” is not of the expected type. |
11 | You cannot make this request. |
const messages = {
__userMessages: {},
1: { en: 'The request is invalid.', it: 'La richiesta non è valida.', },
2: { en: 'You cannot filter results by the “{key}” parameter.', },
11: { en: 'You cannot make this request.', }
}
4. BrowserLanguage Symbol
const BrowserLanguage = Symbol('BrowserLanguage');
Use this symbol in the options.language
field of apiRoute()
to force all responses to a given locale, ignoring the client’s Accept-Language
header.
5. Utility Functions
5.1. defineMessage(number, value)
Register or override a set of translations for message code number
.
-
Parameters
• number
(Number): The message code to define.
• value
(Object): { [langCode]: 'Translated text', ... }
.
-
Returns
• undefined
(modifies internal messages.__userMessages
).
defineMessage(12, {
en: 'Custom error occurred.',
es: 'Ocurrió un error personalizado.'
});
5.2. message(number, lang, replace)
Retrieve a translated message by code, with optional placeholder replacement.
-
Parameters
• number
(Number): Message code.
• lang
(String): Language code ('en'
, 'it'
, etc.).
• replace
(Object): { key: 'value', target: 'fieldName' }
.
-
Returns
• String
: The localized, interpolated message.
message(3, 'fr', { target: 'nom' });
6. apiRoute() Factory
6.1. Purpose & Usage
Generate an Express middleware that provides CRUD endpoints on a Mongoose model with:
- Query filtering & validation
- Pagination
- Field translation & omission
- Per-method middleware hooks
- Multilingual error messages
import express from 'express';
import mongoose from 'mongoose';
import apiRoute from './index.js';
const User = mongoose.model('User', new mongoose.Schema({
name: String,
age: Number
}));
const app = express();
app.use(express.json());
app.use(
apiRoute(User, {
methods: ['GET','POST','PUT','DELETE'],
pagesManager: { maxResults: 100 },
acceptedQueryFields: ['name','age'],
fields: { name: { en: 'name', it: 'nome' } },
options: {
POST: {
middleware: async ({ document }) => {
document.createdAt = Date.now();
}
}
}
})
);
app.listen(3000);
6.2. Options
model (first arg) | Mongoose Model | — | The target model for CRUD. |
filter | Function | Function[] | [] | Pre-handler checks. Return true to continue; false or object for error. |
methods | String[] | ['GET','POST','PUT','DELETE'] | Allowed HTTP methods. |
route | String | '/api/{collectionName}' | Base path ({modelName} , {collectionName} placeholders supported). |
fields | Object | null | Map model fields to custom names per locale. |
pagesManager | Object | undefined | { limit: '?limit', page: '?page', maxResults } for pagination. |
acceptedQueryFields | String[] | Object | model.schema.paths | Fields allowed in req.query / req.body . |
throwRefusedQueryFields | Boolean | true | 400 on unallowed query fields. |
language | String | Symbol | req.acceptsLanguages()[0] | Force locale if not BrowserLanguage . |
options | Object | {} | Method-specific: |
| | | • options.GET , options.POST , etc. |
6.2.1. options[method]
sub‐options
middleware | Function | Function[] | Runs before saving/updating. Receives { document, req, res, next, query } . |
skimming | Function | Function[] | Post‐query filter: return true to keep each document. |
6.3. Handler Flow
- Initialize options: normalize
filter
, methods
, route
.
- Incoming request → determine language (override if
options.language !== BrowserLanguage
).
- Merge method‐specific
options[method]
.
- Parse & validate query/body via
parseFilter()
:
- Rename fields
- Enforce
acceptedQueryFields
- Apply pagination parameters
- Run each
filter
function → may short‐circuit with 403/custom error.
- Dispatch by HTTP method:
- GET →
Model.find()
, optional skimming, field translation, JSON result + paging.
- POST → new document,
middleware
, .save()
, skimming, field translation.
- PUT →
.findOneAndUpdate()
, middleware({ query, set })
, re‐fetch, skimming, translation.
- DELETE → find matching docs, optional skimming,
.deleteOne()
/.deleteMany()
.
6.4. CRUD Operations
GET | .find(query).sort().skip().limit().lean() → skimming() → translate → { ok: true, [collection]: [...] } | Results array + optional pagesManager info |
POST | new model(query).save() → skimming() → translate → { ok: true, document } | Newly created document |
PUT | findOneAndUpdate(query, set) → re‐fetch → skimming() → translate → { ok: true, modelName: document } | Updated document |
DELETE | .find(query).lean() → skimming() → deletion → { ok: true } | Confirmation |
6.5. Error Handling
- Invalid options → thrown synchronously (e.g. non‐array
methods
, invalid route
type).
- Filter rejection →
403
or custom payload.
- MongoDB ValidationError → aggregated into
400
with per‐field errors using localized messages (codes 3–10).
- Unallowed query fields →
400
with error code 2.
7. Sequence Diagram: GET Request Flow
sequenceDiagram
participant Client
participant ExpressJS
participant Handler as "apiRoute Handler"
participant Model as "Mongoose Model"
participant DB
Client->>ExpressJS: |"GET /api/items?name=John&limit=10&page=2"|
ExpressJS->>Handler: |"invoke apiRoute(model, options)"|
Handler->>Handler: Determine language (Accept-Language or forced)
Handler->>Handler: parseFilter(req.query)
Handler->>Handler: Validate acceptedQueryFields
Handler->>Handler: Apply pagination → limit, page
Handler->>Handler: Execute filter functions
Handler->>Model: find(query).sort().skip().limit()
Model->>DB: Execute MongoDB query
DB-->>Model: Return documents
Model-->>Handler: Lean results
Handler->>Handler: skimming(results)
Handler->>Handler: Translate field names
Handler->>Client: Return JSON `{ ok:true, items: [...], pagesManager }`
8. Usage Example
import express from 'express';
import mongoose from 'mongoose';
import apiRoute, { defineMessage, BrowserLanguage } from './index.js';
const productSchema = new mongoose.Schema({
title: String,
price: Number,
category: String
});
const Product = mongoose.model('Product', productSchema);
defineMessage(2, { en: 'Filtering by “{key}” is not permitted.' });
const app = express();
app.use(express.json());
app.use(
apiRoute(Product, {
methods: ['GET','POST','DELETE'],
fields: {
title: { en: 'title', es: 'titulo' },
price: { en: 'price', es: 'precio' }
},
acceptedQueryFields: ['title','price'],
pagesManager: { limit: '?limit', page: '?page', maxResults: 50 },
options: {
POST: {
middleware: async ({ document }) => {
document.createdAt = new Date();
}
}
},
language: BrowserLanguage
})
);
app.listen(3000, () => console.log('API listening on 3000'));
9. Exceptions & Errors
filter not function or array of functions | apiRoute(model, { filter }) -> filter must be a function, or an array of functions |
methods not an array | apiRoute(model, { methods }) -> methods must be an array of methods |
Invalid HTTP method in methods | apiRoute(model, { methods }) -> invalid method "<METHOD>" |
route not a string | apiRoute(model, { route }) -> invalid route, it must be a string |
Unallowed query field (by default) | 400 JSON { ok:false, status:400, error: message(2) } |
MongoDB ValidationError | 400 JSON with errors: [ { target, error } ] using codes 3–10 |
Enjoy building multilingual, flexible REST APIs with zero boilerplate! 🚀