Node.js, Express, Mongoose - input validation - within a route or model?

I have an api rest resource that accepts a JSON message. Example:

{ "location": { "coordinates": [ -122.41941550000001, 37.7749295 ] } 

Coordinates are then collected from an Express request:

 module.exports.create = function(req, res, next) { var coordinates = req.body.location.coordinates; .... 

They are then passed to the Mongoose model. I am writing tests against this where location.coordinates coordinates are missing, for example.

 { "foo": { "bar": [ -122.41941550000001, 37.7749295 ] } 

Then this is not done in the model validation section:

 locationSchema.path('location.coordinates').validate(function(coordinates){ ^ TypeError: Cannot call method 'validate' of undefined 

So my question is, how can I verify that the input is correct? Should this be done on the route before getting into the model, or should it be done in the model? Any examples of how one might appreciate.

For reference, the Mongoose model looks something like this:

 var locationSchema = new Schema({ userid: { type: Number, required: true }, location: { type: [{ type: "String", required: true, enum: ['Point', 'LineString', 'Polygon'], default: 'Point' }], required: true, coordinates: { type: [Number], required:true } }, create_date: { type: Date, default: Date.now } }); locationSchema.path('location.coordinates').validate(function(coordinates){ ... }, 'Invalid latitude or longitude.'); 
+5
source share
2 answers

My typical approach is to implement a service layer between routes and the model and that where validation is done. Do not think of β€œservice” in the sense of β€œweb service”; it just provides a level of abstraction around a given domain. This has the following advantages:

  • This gives you a general abstraction for working with persistent and / or external data. That is, regardless of whether you interact with data from Mongoose or an external web service, all your route logic can simply interact with a consistent interface.
  • It provides encapsulation of sound around the details of persistence, allowing you to complete the implementation without using all of your routes.
  • It allows you to reuse code with non-route consumers (for example, an integration kit).
  • It provides a good level for bullying (for example, for use with unit tests).
  • It provides a very clear level of "validation and business logic" here, even when your data is distributed across several different databases and / or backend systems.

Here is a simplified example of how this might look:

location-service.js

 var locationService = module.exports = {}; locationService.saveCoordinates = function saveCoordinates(coords, cb) { if (!isValidCoordinates(coords)) { // your failed validation response can be whatever you want, but I // like to reserve actual `Error` responses for true runtime errors. // the result here should be something your client-side logic can // easily consume and display to the user. return cb(null, { success: false, reason: 'validation', validationError: { /* something useful to the end user here */ } }); } yourLocationModel.save(coords, function(err) { if (err) return cb(err); cb(null, { success: true }); }); }; 

some-route-file.js

 app.post('/coordinates', function(req, res, next) { var coordinates = req.body.location.coordinates; locationService.saveCoordinates(coordinates, function(err, result) { if (err) return next(err); if (!result.success) { // check result.reason, handle validation logic, etc. } else { // woohoo, send a 201 or whatever you need to do } }); }); 

I applied this structure to 3 or 4 different web applications and APIs at that moment and loved it.

+6
source

In my opinion, validation should happen at the very beginning, on the client first, then on the route.

There is not much interest in transferring invalid data using resources for nothing, so the sooner you specify it as invalid, the sooner you will free resources.

to check the availability of your coordinates, you can use:

 if(req.body.location.coordinates){ //do your thing } 
0
source

Source: https://habr.com/ru/post/1204290/


All Articles