Monday, August 17, 2015

My TODO List with MongoDB, Express, Angular and Node

MEAN Is as MEAN Does!

The motivation for this paper is to continue with my training into the MEAN stack of technologies.  Recently I had the pleasure to meet and share some learning time at the Vancouver MongoDB User Group (MUG).  The meeting was sponsored by Light House Labs; they donated the space and the very much appreciated, classic-meet-up-cannot-be-without: pizza and "healthy drinks" (some sort of zero calories patented combination of salts and flavors).  

The meeting was in one word, inspiring:  there is so much to learn!  So much to share!  That is how we landed on the moon!  Talking, sharing...  oh, man!  From there, I said to myself: "Ok, ok, I am going to do the new "Hello World", The "One Ring to rule them all":  a TODO list using REST and MEAN.

TODO: Mongoose Schema

The Todo Schema has three properties: title as String, completed as Boolean and createdOn as Date.  Note that createdOn has a default value of Date.now.  Also note that the title is required and that it has custom validation.



The thought of using createdOn was to at some point allow only one Todo of a type per day, like I have breakfast every day only once, the second time one eats breakfast food it is not breakfast time but something else...  lunch perhaps?  I love breakfast food for dinner!!!

In the future I am going to play with OATH and an authentication middleware so different users can have their own Todos.

TODO: Node REST API

What is new here?  Nothing, but it is mine!  It is worth mentioning that this route controls all the REST calls.




TODO:  Server

Here is the server.  The server is using the router middleware.  It is important to note that the router needs to be setup before the 'catch all' route app.get("*", function(req, res){ ... });.  The idea is that all calls are handled with the index page and the REST AJAX done by Angular is handled by the route middleware.

Interesting is to note that the connection to MongoDB is not stating the port number.  I assume that Mongoose tries the default port 27017 first hand and if the service is running on that port then uses it and everything is hunky-dory.





A section of code in the server is commented out.  I was not able to successfully use "flash" middleware with the "jade" view engine to display error messages.  So for now I am sending a json message to the client in the case that there is an error and allow the Angular client to display it perhaps using a popup window using the Angular Modal window service...  it would be ok, I guess.

TODO Controller

The controller is pretty fat I must admit.  It does everything I need it to do however,   Note that the Mongoose Todo has been injected, you probably saw that in the REST router.  Things to note in the controller:

  • Post:  Uses find (query, callback).  If not found then Todo.save(callback) invoked.
  • Get: Gets all Todos using Todo.find({}, callback).  Note that the query is empty.  Using the empty query is optional.
  • Update:  This is to be used with REST PUT.  Obviously the item is updated only if found, so this function uses Todo.findById(id, callback).  The Todo is saved in the callback.  This update is done when the user toggles the "done" checkbox".  I decided to keep the done Todo until purged.
  • Purge:  Purge is a bit more interesting, so let us take it aside.


todoController.purge

The Angular client $http service invokes the API to purge then in the Node controller the Todos collection is queried for "{completed : true}".  If the query returns something then we call Todo.remove on the entire collection BUT note that this is done with only one call to MongoDB and with a query parameter using the $in operator.

The $in operator selects the documents where the value of a field equals any value in the specified array of Todos which have completed ===  true then we call remove on them.

This is very cool.

So, what else can I show you?  Hum...  ah!  Ok, the Angular app.

TODO:  Angular Factory and Angular Controller

Nothing to it, I like how trimmed it looks.  I wish I was as trimmed and strong as one of my factories!  I would look like Terminator, the good one, the one that John Connor sent to protect his mother and himself when he was a kid in T2.  Yeah!  I like that!


and the controller is this one:


The Jade View

If you want to put it all together here the jade view.  It is a little wonky, I have to make it work a little better with errors...  I do not like what I have there. 


I notice right now that I have to move the JS code reference to the end of the file....  hey, would do that for me pal? 

:)

My MEAN Dev Tool

It seems like months since I first downloaded and installed Visual Studio Code for Node.js related development but this journey started just a few days ago on July 30st!  I guess that we get used to good things very fast.  Since then I have uninstalled all code editors:  Visual Studio Code is here to stay on my computer and that is that...  Sure it does not have the super advanced features that other product bring, but heck, it is free and it works!



Fellows, I tell you; it has intellisense, you can debug your Node.js code, place brake points.  I hope that Microsoft continues enhancing this product!




What is Next?

I could make my view better, perhaps prettier.  I want to plug in my Mocha test and add Passport to other team members can have their own Todos.

Ah!  In the case that MUG lets me, I can use this to talk about things at the user group.

Get MEAN TODO From GitHub

You can always clone the project from GitHub; here:



References:

  1. First time I use Visual Studio Code.  This note contains a reference to where to get the tool
  2. Nodding since...  not that long ago:  NodeSchool gave me the bug!
  3. Using $in:  MongoDB $in operator

Sunday, August 2, 2015

Mongoose Schema Validation

Bad Data Is No Better Than No Data:  Then, Validate Ebenezer!

After my previous post, Noding, MongoDBing With Mongoose: On the Account of the MVA and Visual Studio Code, the natural thing to do was to validate the data model before a save or an update.  Let us get to it then!

It is always good practice to validate application data in the logic layer but it should be a hard requirement for any solution to enforce data validation on the model layer just before the data is saved.  MongoDB does not provide data validation, therefore data validation is on us; we are responsible for it.

Using the same Mongoose Data Model defined in the previous note, let us see how many things can go wrong with our data before adding data validation.  So, we had BankData with a collection of Accounts.  Can BankData be saved, updated without a FirstName or LastName?  Can a new Account be added without a type?  Or perhaps add a negative amount to an account?  Oh, yes, it can be done!  The Mongoose Schema has no validation!  That's not good; darn bank!


The client was able to save BankData with no first_name and no last_name and later on it was updated with a new account with no type.  

I get it, I need validation, so what types of validation Mongoose offers?  First off, validation is defined in the schema and it takes place when a document attempts to be saved, after default values have been applied .  Therefore, if our Schema would have contain validation then the above saves, without changing the code at all, would have never work and the error would have been passed to the callback, which means that assert.equals(null, err) would have failed.

Built-in SchemaType Validation

Luckily for me built in validation into the SchemaField can be applied to make these fields required.



If the client tries save the bad data then Mongoose, before the save, executes the validation, detects the error and the data is not saved.  Nice!  But there is more, the String type take s list of allowed values therefore I could place here the currencies allowed by the bank, say USD and CAD only.



Custom Schema Validation

But, what about if this bank accepts all currencies of the world,except... the British Pound! then the above validation would be unreasonable because the enum array for currency would be really, really long.  Ah!  Instead we could use a custom validation and test only for the invalid ones!  How does that work?  Easy!



Line 13 above is passing a validation function and an error type to the model SchemaType.  The currency could be anything but GBP.  This is a bit of a problem because now anything goes but the little £; even made up currencies could be set which is truly wrong.

Mongoose Middleware

Using Mongoose middleware we can validate the account currency type by intercepting the save process in a pre-save event and test the currency set by the client against a collection currencies previously set in MongoDB.  A middleware is like an interceptor which inserts its execution before or after the process execution.  The pre-save would be like catching the save on the fly.  This would be useful for the execution of complex validation or for setting some properties of the object just before save.


A Pre-save middleware executes before the save operation and if everything is OK what happens next is the actual save.


The way to implement a pre-save middleware for the account is like this:





A few things to notice from the above middleware;  First, there is a new Mongoose Schema, the validCurrencyModel (line 2).  This model represents a valid currency used by the bank.  The bank collection of valid currencies might not change very often but I decided to store it in the database for better management.  I'll show you how I batch load them to the MongoDB in a little later.

Note that in line 4 the middleware is executing a query for currency of the account (this.currency) on Currency to find out if the currency set by the client is valid.  If the currency is not found then an error is issued of the type InvalidCurrency  and the next thing that happens is that the error is returned to the callback of the save (line 11).  If the currency type is valid then the middle ware calls next() which means "carry on to the next step, the save, this data is good".

Batch-Loading Currency

A Currency object was created.  Note three things in the following code section:  currency_symbol is required, has custom validation and is unique.



Note that the batch was done by creating an array of Currency and passing it to create.  Create returns to the callback the error and the entire collection of created documents.  Simple.



Conclusion

In this note I have used Mongoose validation in three different ways to validate the Data Model:

  • Validation at SchemaType level.  We can use required, allowed value enums for strings. Max and Min for Number types.  In the case of Currency the SchemaType for currency_symbol was also set to "unique: true" in order to avid duplicates.
  • Custom Validation: By passing a validation function to the SchemaType
  • Using Mongoose Middleware:  Validation of the model in a pre-save event.
This note shows only a taste on Mongoose validation.  You have probably noticed that to test my Mongoose model I have been using scripts which are not very useful.  I feel that the natural continuation of this Node + MongoDB + Mongoose would be to use the defined model in a system through a RESTful API using Express...  hum, I would not be surprised if I soon take on this.

References

A1 Repo: Simple Pagination for WCF Service Operation

It is never a good idea to paginate on the client.  This post is about a simple pagination for a WCF service operation.  For this work I nee...