Managing Mongoose Projections in NestJS

Subhadeep Datta - Feb 18 - - Dev Community

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

  1. Combined Auth/Profile Models: User authentication and profile data lived in a single schema (a known anti-pattern, but we all cut corners sometimes 😬).
  2. Leaky Projections: Sensitive fields like password and OTP slipped into API responses through populated relationships.
  3. 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 };
Enter fullscreen mode Exit fullscreen mode

Step 2: Reusable Population Blueprints

// database/populations.config.ts  
export const TrainingMaterialPopulations = [  
  {  
    path: 'createdBy',  
    select: UserSafeProjection  
  },  
  {  
    path: 'project',  
    populate: {  
      path: 'organisation',  
      select: EntityBaseProjection  
    }  
  }  
];
Enter fullscreen mode Exit fullscreen mode

Step 3: Cleaner Service Implementation

async findById(id: string) {  
  return this.trainingMaterialModel  
    .findById(id)  
    .populate(TrainingMaterialPopulations)  
    .select(EntityBaseProjection)  
    .lean();  
} 
Enter fullscreen mode Exit fullscreen mode

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!

.