.lean() plain objects vs hydrated documents for latency-bound Mongoose queries
0 reputation · 28 Sept 2024, 06:29 UTC
For a read-heavy endpoint returning large result sets from a Mongoose collection, the choice between .lean() and hydrated documents creates a direct tension between raw throughput and model-centric design. Hydrated documents preserve schema virtuals, getters, instance methods, and change tracking, but every result pays an instantiation cost that scales with collection size. Switching to .lean() returns plain JavaScript objects and lowers per-result CPU and memory overhead, yet it bypasses toJSON and toObject transforms and skips document middleware such as post('init').
When business logic is encapsulated in schema methods or virtuals, .lean() forces that logic to be duplicated in application code or reapplied in a post-query mapping step. Because Mongoose does not offer a partial-hydration mode, the decision is binary per query. TypeScript typings and default schema options have shifted across major versions, so compile-time and runtime behavior may differ from older examples.
Given these constraints, which approach is more appropriate when an endpoint requires both low latency and reusable schema encapsulation? Does combining .lean() with a projection introduce maintainability risks that outweigh the performance gains? How should teams confirm that serialization behavior remains consistent across Mongoose major versions when adopting .lean()?