The issue at hand involves the challenge of constructing intricate objects based on specific parameters or inputs, a dilemma often addressed through the utilization of a design pattern referred to as the builder pattern.
While incorporating such patterns may introduce a level of complexity to your code (refer to this for an example), below is a straightforward implementation:
"use strict";
const queryBuilder = () => {
const query = {};
return {
addLocation: function(location){
if(typeof location === "string" && location.length>0){
query.location = location.trim();
}
return this;
},
addDate: function(date){
const dateObj = date ? new Date(date) : null;
if(dateObj && !isNaN(dateObj.valueOf())){
query.date = dateObj;
}
return this;
},
build: function(){
Object.freeze(query);
return query;
}
}
}
app.get('/:location/:date',async(req,res,next) => {
//Make sure you have a connected db instance for this part
const db = getDbSomehow();
const myQuery = queryBuilder()
.addLocation(req.params.location)
.addDate(req.params.date)
.build();
const results = await db.collection.find(myQuery).toArray();
//Proceed with necessary actions
})
Although this may seem more verbose compared to alternative solutions you've encountered that still function, there are definite advantages to this approach:
- Enhanced clarity in your request handler.
- You can consolidate all validations within the builder's methods.
- Want to include another property in the query? Simply implement the corresponding method in the builder, call it, and you're set.
- Interested in altering the sequence of keys in your query object? Adjust the order in which the builder's methods are invoked.
- Immutable nature: Utilize
Object.freeze()
or a tool like deep-freeze within your build
method to maintain the object's immutability once finalized.