Why You Can’t Directly Modify Mongoose Query Results and How I Learned It the Hard Way

Photo by Ilyas on Unsplash

Why You Can’t Directly Modify Mongoose Query Results and How I Learned It the Hard Way

All I had to do was rename a field in the result of a Mongoose query before sending a response to the client. However, no matter what I tried, I couldn’t get it. After spending about 3 hours trying to figure out what could be wrong, I realised something — you cannot directly modify Mongoose query results. Here’s a full breakdown of the problem and what I learned.

My task was to refactor some parts of the codebase. The last thing I had to do was rename a field in the result of a Mongoose query before making a PR. I thought it would be a walk in the park. I thought all I had to do was modify the query result (an object of course) by setting the field to a new field (or property) and then delete the former. In my case, it was changing roadmapContent to learningContent. So I wrote something like this:

roadmapId.learningContent = roadmapId.roadmapContent
delete roadmapId.roadmapContent;

Then I ran a test to check if it worked, to my surprise, the field was still roadmapContent. I was confused because, of course, that’s the only logical to modify the query result. So, I checked the console to see if there were any errors, but nothing.

Then I added console.logs before and after the lines where I wrote the code because it was inside an if statement so I thought maybe the condition was false but to my surprise, the logs appeared. Then, what could be wrong? I don’t know so I ran to ChatGPT for help. I pasted my code and asked for help but the responses weren’t helpful. I got tired of the responses so I went to Claude, the same thing. Finally, I decided to try Copilot but this one was the worst here. It was just giving me back my code and didn’t provide any potential solution.

I thought maybe due to exhaustion, I missed the changes but bro actually gave me back my code.

You can tell that I was frustrated from the screenshots above. I tried the suggested solutions by ChatGPT and Claude, made several console.logs, checked Google and Stack Overflow for how to modify Mongoose query results but no success.

Frustrated and tired, I was inspired (Alhamdulilah) to start a new chat and ask:

I tried the suggested solution and it worked! I can’t even express how happy I was. I knew there’s this saying in programming that if your code works, just leave it but I had to understand why it worked so I asked my favourite LLM,

I realised that the reason my approach of directly modifying the query object returned by Mongoose didn't work is because, by default, Mongoose queries return a Mongoose document that contains the raw data retrieved from the database, methods such as save() and some internal logic that makes the document properties immutable.

The abstraction or internal logic is important because it lets Mongoose make the query result consistent with the schema definition. So even though when I tried to directly modify the query result, it worked (or so I thought), it wasn't reflected in the final result because Mongoose doesn't allow arbitrary modifications of fields.

So, the solution was to convert the returned Mongoose document to a plain old JavaScript object using .toObject() that removes all Mongoose’s internal methods and schema-based restrictions so you can freely modify the object like any normal JavaScript object. For example,

const user = await User.findById(id);
const userObject = user.toObject();
userObject.name = userObject.firstName;
delete userObject.firstName;

But another approach that is more recommended is to use .lean() in your query. This lets Mongoose return a plain JavaScript object directly instead of a Mongoose document. Hence, it is faster as it skips document instantiation. Also, it simplifies the query. For example,

const user = await User.findById(id).lean();
user.name = user.firstName;

You can learn more about the lean option here. You can also find the full explanation by ChatGPT here.

Of course, I knew prior that Mongoose doesn’t return a normal JavaScript object, I also knew about the lean option. But amid everything, I had forgotten this critical detail. It was a frustrating and time-consuming experience, but it served as a valuable reminder to revisit the basics when things aren’t working as expected. I hope this post helps someone.