Recently, I was working on a new typescript project and I needed to add a feature. I immediately realized I had done something similar in a previous project and I quickly opened the project, copied the lines of code I needed from it and pasted it in the new project. It didn’t work! :) The previous code was in JavaScript and the new project is in typescript.
The first problem I had was with mongoose. I tried writing a pre-save middleware the javascript way but it didnt work.
UserSchema.pre("save", async function (next) {if (!this.isModified("password")) {next();}const salt = await bcrypt.genSalt(10);this.password = await bcrypt.hash(this.password, salt);});
Above is a middleware written in javascript and below is the same middleware written in typescript
schema.pre('save', async function (done) {if (this.isModified('password')) {const hashed = await Password.toHash(this.get('password'));this.set('password', hashed);}done();});
Notice that in the javascript version, I was able to set a variable directly but in the typescript version, I had to call the set method on the object.
this.password = await bcrypt.hash(this.password, salt); //javascript
this.set('password', hashed); //typesript
The next problem I had was with the geo-coding data. So I had this model with different fields and also the location field. this location field contained the geolocation information that I wanted to save. It looked something like the code below in javascript
....//Code omitted for brevity
location:{type:{type: String,enum: ['Point'],required: true,},coordinates: {type:[Number],required: true,index: '2dsphere'},formattedAddress: String,street: String,city: String,number: String,zipcode: String,country: String},
....//Code omitted for brevity
The presave middleware looked like below
schema.pre('save', async function (done) {const loc = await geocoder.geocode(this.get('address'));this.set('location', {type: 'Point',coordinates: [loc[0].latitude, loc[0].longitude],formattedAddress: loc[0].formattedAddress,street: loc[0].streetName,city: loc[0].city,state: loc[0].stateCode,zipcode: loc[0].zipcode,number: loc[0].streetNumber,country: loc[0].countryCode,})done();});
For some reason, this code kept failing. But it was passing in the Javascript version. What I did was to make the coordinates and type fields’s required field to be false. so now the code now looks like below
....//Code omitted for brevity
location:{type:{type: String,enum: ['Point'],required: false,},coordinates: {type:[Number],required: false,index: '2dsphere'},formattedAddress: String,street: String,city: String,number: String,zipcode: String,country: String},
.....//Code omitted for brevity
And for some reason, it works…..
I honestly do not know why
Now on to the main issue that I am currently facing and that is writing a middleware that fires when an update is made to the address field. I have tried a lot of solutions but none seem to be working and the only fix i have been able to come up with is to make the update in the controllers. I dont know if this is an optimal solution. I am not sure actually since I am just starting out in this field but the fix can be found below nonetheless.
//Make sure to update geocoded address when a user updates the //address. For some reason, I cant get this to work as a mongoose middleware//Check if address is actually being updated
if(req.body.address){const newAddress = await geocoder.geocode(req.body.address)const user = await DB.Models.User.findById(id);req.body.location = user?.locationreq.body.location.coordinates = [newAddress[0].latitude, newAddress[0].longitude]req.body.location.street = newAddress[0].streetNamereq.body.location.formattedAddress = newAddress[0].formattedAddressreq.body.location.city = newAddress[0].cityreq.body.location.state = newAddress[0].stateCodereq.body.location.number = newAddress[0].streetNumberreq.body.location.zipcode = newAddress[0].zipcodereq.body.location.country = newAddress[0].countryCode}
Normally, I am a presave middleware that sets the location field based on the response gotten from the call to the geocoding API and it can be found below
schema.pre('save', async function (done) {const loc = await geocoder.geocode(this.get('address'));this.set('location', {type: 'Point',coordinates: [loc[0].latitude, loc[0].longitude],formattedAddress: loc[0].formattedAddress,street: loc[0].streetName,city: loc[0].city,state: loc[0].stateCode,zipcode: loc[0].zipcode,number: loc[0].streetNumber,country: loc[0].countryCode,})done();});
But I dont know why any middleware is not working when I am trying to fire it when an update is made to the address field. I will update this article as soon as I find a better solution. Also if you have a better solution, please feel free to reach out to me (owo.ezekiel@gmail.com) or put it in the comments. Lets learn together
I apologize for the brevity of these code snippets. This article is not meant to be an introduction to any of the technologies used in this project. It is just meant to help shed light on some javascript to typescript transitions. I had to google a lot to find this fixes and I will keep googling because thats just what I do lol.
If you found this article useful please drop a clap. If you have any suggestion on how to make this article better, please reach out to me or drop a comment.
See you next time….