The Backstory
While building a module in NestJS, I encountered a common dilemma: How to secure sensitive data in populated documents without overhauling our existing architecture?
Here’s how I addressed it pragmatically, while acknowledging room for improvement.
The Problem, Simplified
- Combined Auth/Profile Models: User authentication and profile data lived in a single schema (a known anti-pattern, but we all cut corners sometimes 😬).
- Leaky Projections: Sensitive fields like password and OTP slipped into API responses through populated relationships.
- Repetitive Code: Similar field exclusions were duplicated across multiple queries.
The Middle-Ground Solution
Instead of fully refactoring our auth system (which would’ve been ideal), I focused on immediate risk mitigation:
Step 1: Centralized Projection Configs
// database/projections.config.ts
export const UserSafeProjection = {
password: 0,
otp: 0,
email: 0,
mobileNumber: 0,
__v: 0
};
export const EntityBaseProjection = { __v: 0 };
Step 2: Reusable Population Blueprints
// database/populations.config.ts
export const TrainingMaterialPopulations = [
{
path: 'createdBy',
select: UserSafeProjection
},
{
path: 'project',
populate: {
path: 'organisation',
select: EntityBaseProjection
}
}
];
Step 3: Cleaner Service Implementation
async findById(id: string) {
return this.trainingMaterialModel
.findById(id)
.populate(TrainingMaterialPopulations)
.select(EntityBaseProjection)
.lean();
}
Why This Works (For Now)
Pros | Cons |
---|---|
Immediate security improvement | Still need proper auth separation |
Reduces code duplication | Partial technical debt remains |
Easy to maintain/update | Projections ≠ full authorization |
Lessons Learned
- Layer Your Security: Projections are just one piece of the puzzle.
-
Document Tradeoffs: Added JSDoc:
// TEMP: Remove after auth refactor (Q4?)
- Start Small: Even partial solutions can reduce risk while you plan bigger fixes.
Next Steps (If I Weren’t Lazy)
- Separate auth into its own service/collection.
- Implement proper role-based access control.
- Add encryption for sensitive fields.
Your Thoughts?
How do you balance immediate fixes with long-term ideals? Have a better pattern for handling nested projections? Let’s discuss!