Upgrading
Deprecations in 3.3, and moving from 2.x to 3.x.
Which section applies to you#
| You're on | Read | Effort |
|---|---|---|
3.0 – 3.2, using createRouterS3upload | Uploads move into createRouter | Minutes. Not urgent until 4.0. |
3.x, using legacyMode or other deprecated APIs | Before 4.0 | Depends on how much you rely on it. |
| 2.x | From 2.x to 3.x | An afternoon. Security release — urgent. |
The complete guide, with every change explained, is MIGRATION.md; release notes are in CHANGELOG.md.
Uploads move into createRouter (3.3)#
createRouterS3upload is deprecated in 3.3 and removed in 4.0. It still works, and emits a DeprecationWarning (code ECS_DEP001) once per process. Move path, fields and imgOptimizations into upload, next to the settings already there:
createRouterS3upload({
model: Product,
path: 'products/',
fields: [{ name: 'cover', maxCount: 1 }],
imgOptimizations: 'medium',
upload: { acl: 'public-read' },
});
createRouter({
model: Product,
upload: {
path: 'products/',
fields: [{ name: 'cover', maxCount: 1 }],
imgOptimizations: 'medium',
acl: 'public-read',
},
});
createRouter ignores path, fields and imgOptimizations at the top level and logs a warning if it sees them. In TypeScript, replace RouterS3Options with RouterOptions.
Before 4.0#
Everything here still works in 3.x. The first two are removed in 4.0; the rest are deprecated, so move off them when you touch the code.
| Deprecated | Replace with |
|---|---|
createRouterS3upload(opts) | createRouter({ upload }) — above |
legacyMode: true | The 3.x options it bypasses: allowedFields, filterableFields, sortableFields, maxLimit. |
new ControllerSets(Model, '-createdAt', ['category'], ['name']) | new ControllerSets({ model: Model, orderBy: '-createdAt', query: ['category'], search: ['name'] }) |
controller.getById · controller.queryAll | controller.get · controller.query |
fileUploadMiddleware(req, res, next, path, fields, level) | fileUploadMiddleware(req, res, next, { uploadPath, fields, imgOptimizations }) |
otp.deliver | mail.sender / sms.sender — Email & SMS |
From 2.x to 3.x#
If you run 2.x on a public endpoint, treat this as urgent. In 2.x a client could write any schema field, filter and sort on fields your API never returned, hang the database with a crafted search term, read entire collections in one request, and upload world-readable HTML to your bucket. Every breaking change in 3.0 is a default that used to be unsafe.
Deploy today, tighten after#
createRouter({ model: Product, legacyMode: true });
legacyMode restores 2.x behaviour so you can upgrade immediately, then remove it router by router as you add the options below. It re-enables every vulnerability listed, so don't leave it on.
What changed, and what to do#
| What changed | What to do |
|---|---|
| Request bodies are filtered | Add allowedFields or blockedFields to every router. |
?compareField=, ?rangeField=, ?sort= are allowlisted | List fields in filterableFields / sortableFields, or rely on the query default. |
| Search terms are escaped and length-capped | Nothing — unless clients sent real patterns; then allowRawRegex: true, behind auth. |
| Unpaginated reads capped at 100 | Paginate, or raise maxLimit deliberately. |
| Relational search resolves the full nested path | Re-check depth-2 paths — they queried the wrong field before. |
String-form search reads ?s= | Update clients that searched with ?title=. |
| Uploads default to private, type sniffed from bytes | Set upload.acl and upload.allowedMimeTypes if needed. Existing objects keep their old ACL — audit them. |
| Clients no longer choose compression level | upload.allowClientImageOptions: true to opt back in. |
| GIF and SVG no longer transcoded | Nothing — they were being corrupted. |
| 5xx responses no longer echo internals; duplicates are 409 | Update clients that parsed 500 message text. |
Reads are no longer .lean() | Your toJSON transforms now apply. lean: true restores speed — check what those transforms hide first. |
No dotenv.config() at import; multer-s3 removed | Load your environment yourself; uninstall multer-s3. |
The lean change is the one most likely to alter responses. 2.x list endpoints bypassed toJSON transforms. If you strip sensitive fields there, 2.x was leaking them on every list request, and 3.x stops. Compare a list response before and after upgrading.