BT

New Early adopter or innovator? InfoQ has been working on some new features for you. Learn more

A Rails Enthusiast’s take on MEAN.js

| Posted by John Troxel on Oct 03, 2014. Estimated reading time: 15 minutes |

I’ve been developing web applications for a while. I don’t often fall in love with the latest hot technology: there are always pros and cons, and always alternatives around the corner. The last time I found myself enamoured was when I discovered Rails several years ago. Rails was special in many ways, and it took the web development world by storm. Now I am beginning to get that same feeling for some new technologies: the AngularJS framework and the MEAN.js stack. In this article I will explore this new stack, and recreate the demo that put Rails on the map.

About 7 years ago, I had heard of Ruby on Rails, but not given it much thought. When I landed a gig that involved Rails work, I was prompted to watch the famous DHH blog demo… I was blown away. I embraced this new world: I remember devouring a couple of the Prag books, even sneaking some chapters while away for a weekend with my wife. For me, the reason that Rails became so popular is simply this: the framework did a great job of making the common tasks of creating web applications simple and enjoyable. “Convention over configuration,” powerful code generators, and the beauty of the Ruby language made development fast and fun.

In the years since, there have been many frameworks and stacks doing very similar things to Rails. But nothing has knocked Rails of its perch in my mind, as the single most productive stack for typical web applications. But lately I think a sea change has started in the web application world: single-page design, reactive style programming, and NoSQL databases have become great fits for many newer web applications, and this provides an opening for new solutions. Friends and respected voices have started talking about AngularJS and the MEAN stack, and the increasing mindshare and momentum of these technologies is palpable. I am also personally very intrigued by the promise of sharing logic, specifically JavaScript code, on the client and the server. For all these reasons, I have decided to explore this stack as a potential successor to Rails.

To dive into MEAN, what better way for a Rails fan to get up to speed than by following the path of the famous demo, and creating my own blog application with MEAN.js. A more up-to-date version of the Rails blog exercise, without the “Uoooops,” is the Rails getting started guide. Our journey here will mirror this guide and summarize my comparison with Rails. To follow along with my code, check out the project on GitHub.

Recreating the Blog Demo

Setting Up

Before diving into code generation--the sexy part of cranking out an app in minutes--one has to set up a basic environment. The README on my project provides the exact steps I took to get up and running on my Mac, and I will hit the highlights here. For Rails we would need Ruby and Gems as well as a SQL database. For the MEAN stack, we need Node.js, Mongo, and a few libraries for starters.

The MEAN.js site has all the steps necessary. I used Homebrew to install the NVM environment manager (very similar to RVM), and used nvm to set up a Node.js environment including the npm package manager. These instructions will give you an idea how easy that is. Next up was MongoDB, which has very simple instructions on the site. You will also need to install Bower and Grunt as described on MEAN.JS. With those pieces in place, we can begin the code generation coolness.

Generating the App

To start a new app in MEAN, like Rails, we use a generator. MEAN.js uses Yeoman for automation, and is configured with a generator for a starter application. In this case, I created an application called Blog. I elected to not include the example CRUD module, in order to more closely follow the Rails path. The following shows what generating the app looks like.

     _-----_
    |       |
    |--(o)--|   .--------------------------.
   `---------´  |    Welcome to Yeoman,    |
    ( _´U`_ )   |   ladies and gentlemen!  |
    /___A___\   '__________________________'
     |  ~  |
   __'.___.'__
´   `  |° ´ Y `

You're using the official MEAN.JS generator.
[?] What would you like to call your application? Blog
[?] How would you describe your application? MEAN answer to famous DHH demo
[?] How would you describe your application in comma seperated key words? MongoDB, Express, AngularJS, Node.js
[?] What is your company/author name? codecraft
[?] Would you like to generate the article example CRUD module? No
[?] Which AngularJS modules would you like to include? ngCookies, ngAnimate, ngTouch, ngSanitize
   create app/views/404.server.view.html
   create app/views/500.server.view.html
   … Plus a few dozen more files filling out the structure
npm WARN package.json blog@0.0.1 No repository field.
npm http GET https://registry.npmjs.org/cookie-parser
npm http GET https://registry.npmjs.org/body-parser
   … Plus a couple hundred Node package dependencies
bower angular-resource#~1.2     cached git://github.com/angular/bower-angular-resource.git#1.2.18
bower angular-resource#~1.2   validate 1.2.18 against git://github.com/angular/bower-angular-resource.git#~1.2
bower bootstrap#~3              cached git://github.com/twbs/bootstrap.git#3.1.1
bower bootstrap#~3            validate 3.1.1 against git://github.com/twbs/bootstrap.git#~3
   … Plus a couple hundred client-side JS dependencies

This step is half the magic. Like Rails, the MEAN.js file organization has expressed a strong opinion on the best architecture for a full-stack web application.

The structure created by this humble command looks like this:

+-- LICENSE.md
+-- Procfile
+-- README.md
+-- app
¦   +-- controllers/
¦   +-- models/
¦   +-- routes/
¦   +-- tests/
¦   +-- views/
+-- bower.json
+-- config
¦   +-- config.js
¦   +-- env/
¦   +-- express.js
¦   +-- init.js
¦   +-- passport.js
¦   +-- strategies/
+-- gruntfile.js
+-- karma.conf.js
+-- node_modules
...
+-- package.json
+-- public/
¦   +-- application.js
¦   +-- config.js
¦   +-- humans.txt
¦   +-- lib/
¦   +-- modules/
¦   +-- robots.txt
+-- server.js

The final piece to standing up the shell application is to fire it up, which only takes a silent “grunt:”

$ grunt

...after which you can point your browser to http://localhost:3000. Grunt is a “task runner” that fits into the MEAN ecosystem similarly to how Rake is used in Rails, and by default it is configured to run the Node.js server.

Like the results of the Rails generator, the running MEAN application features a simple landing page with instructions for moving forward, in addition to some links for user management. The MEAN version also builds us a little more in the way of MVC machinery to start with. With a single command I created a running web application and a tree on which to start hanging useful leaves. Next, let’s create the actual blog!

Creating the Blog CRUD

The blog functionality is a pretty typical CRUD situation, where the meat of the application is around editing and viewing the record or model, in this case an Article. The first step to creating a new CRUD module is to fire up another Yeoman generator, which looks like this:

$ yo meanjs:crud-module Post
[?] Which supplemental folders would you like to include in your angular module? css, img, directives, filters
[?] Would you like to add the CRUD module links to a menu? Yes
[?] What is your menu identifier? topbar
   create app/controllers/posts.server.controller.js
   create app/models/article.server.model.js
   create app/routes/posts.server.routes.js
   create app/tests/article.server.model.test.js
   create public/modules/posts/config/posts.client.routes.js
   create public/modules/posts/controllers/posts.client.controller.js
   create public/modules/posts/services/posts.client.service.js
   create public/modules/posts/tests/posts.client.controller.test.js
   create public/modules/posts/config/posts.client.config.js
   create public/modules/posts/views/create-article.client.view.html
   create public/modules/posts/views/edit-article.client.view.html
   create public/modules/posts/views/list-posts.client.view.html
   create public/modules/posts/views/view-article.client.view.html
   create public/modules/posts/posts.client.module.js

You can see the files that were created: we now have models, views, and controllers for both client and server. The coolest part of this generator is that it wires the new module into the UI for us. Looking at our running application, the skeleton Blog UI is up and running:

Creating the Post module with this single generator is the first big divergence from the Rails exercise: where one generates a controller, fills in a little logic, and then builds a simple form. After the controller and view are sketched out, in Rails we would generate a Model and a Migration, and then run the migration to install the schema into the database. This is central to the “Rails way:” using migrations to define database schema and relying on ActiveRecord to provide dynamic logic based on that schema.

I want to point out that it is also possible to generate scaffolding in Rails, and the Rails way is arguably more powerful because it gets specific fields from the database. In addition, what David does in the demo is to use dynamic scaffolding, where the CRUD access is generated at runtime based on the db schema. This was the crux of the wow factor in the original blog demo, and a very useful feature for iterating with users on the domain model. To the best of my knowledge, dynamic scaffolding is no longer supported in Rails. By contrast, the MEAN generator creates the model and all the surrounding CRUD code, but it only sets up a single example field (name). This is a case where I think Rails is ahead of the MEAN stack.

So, we don’t define a database schema and generate stuff from that… how then do we set up the fields for our blog?

Filling in the Post Fields

The MEAN stack uses a NoSQL database, MongoDB. Mongo is not a RDBMS, it’s a document store with really no schema, so the idea of creating objects automatically from a live schema doesn’t work. Instead, the schema that we use is defined in Mongoose, and there are no migrations to build the database since pretty much anything can be stuffed into a document. A Mongoose schema with the fields we want for our posts looks like this:

var PostSchema = new Schema({
    title: {
        type: String,
        default: '',
        required: 'Please fill Post title',
        trim: true
    },
    // calling the body post_body because just body is a pretty common string
    post_body: {
        type: String,
        default: '',
        required: 'Please fill Post body',
        trim: true
    },
    created: {
        type: Date,
        default: Date.now
    },
    user: {
        type: Schema.ObjectId,
        ref: 'User'
    }
})

 

So at this point we need to wire our new schema fields into the application. What I did was search the codebase for “.name” and “name:” and make my adjustments where it made sense. Fortunately, due to the strong conventions expressed in the generated code, it is easy to find code related to Posts: it all could be found under /app/*/post.* (server side) or under /public/modules/post (client side, AngularJS). You can see the changes I made to mimic the Rails app in the commit tagged add_fields_to_posts. Our application now has title and body fields for each Post.

A Quick Tour

At this point, let’s take a quick tour of the functionality that we’ve built. First up is the list view, which I usually lump into the Read part of CRUD.

From the list view, we can dig into a more detailed view, the remainder of the Read aspect. Note that with our simple schema, the details view does not offer much more, but with a more complex data model there could be a lot more information in this view. From this view we can also click delete and edit buttons.

The editing form (Update) looks like the following. The new Post ui (Create) looks very similar, we’ll take a closer look at that shortly.

We’ve talked about the experience of creating our blog app, and looked at the UI that we generated and modified slightly. But what about the code itself, how does MEAN emulate the full-stack nature of Rails? Without getting away from the main thrust of comparing the experience of building the blog, we can hit a few highlights of the code we have created and how AngularJS and ExpressJS are wired together.

For a Rails CRUD module, one probably has to work with 5 files to create functionality around a model, keeping in mind this is for server-side only functionality. In our MEAN blog, there are no less than 14 files for the Post module, split between client and server environments. Well, 14 files is not all that bad: the structure is very logical and the generator did most of the work for us. It is evident that MEAN is stitching together otherwise unrelated client-side and server-side frameworks but it still feels pretty coherent. Let’s look briefly at how these things are wired up.

The essence of MEAN is an AngularJS app for the UI and Express for the back-end that provides REST endpoints for our Posts module. The keystone between the two environments is really the Angular service that provides access to the server-side resource. This is done in posts.client.service.js, shown in its entirety here:

'use strict'; 

//Posts service used to communicate Posts REST endpoints
angular.module('posts').factory('Posts', ['$resource',
    function($resource) {
        return $resource('posts/:postId', { postId: '@_id'
        }, {
            update: {
                method: 'PUT'
            }
        });
    }
]);

Though this doesn’t look like much, it is creating a “resource” that gets injected into the client controller. Since the Express app is set up to follow RESTful conventions, the Angular resource can easily map actions to the back-end. Here is the Angular controller code for listing the Posts, to show how the resource gets used:

// Posts controller
angular.module('posts').controller('PostsController', ['$scope', '$stateParams', '$location',
'Authentication', 'Posts',
    function($scope, $stateParams, $location, Authentication, Posts ) {
// … Lots of other stuff … 

// Find a list of Posts
        $scope.find = function() {
            $scope.posts = Posts.query();
        };

Here the handler for populating the lists simply asks the Posts resource for the full list of posts, the Posts resource created earlier provides the query function wired to the REST endpoint. The routing code on the server necessary to satisfy the the client-side resource, just maps URLs to controller actions:

var posts = require('../../app/controllers/posts');

// Posts Routes
    app.route('/posts')
        .get(posts.list)
        .post(users.requiresLogin, posts.create);

    app.route('/posts/:postId')
        .get(posts.read)
        .put(users.requiresLogin, posts.hasAuthorization, posts.update)
        .delete(users.requiresLogin, posts.hasAuthorization, posts.delete);

The server-side controller code isn’t too involved either, basically it just gets all the Posts and sends them back as JSON.

/**
 * List of Posts
 */
exports.list = function(req, res) { Post.find().sort('-created').populate('user', 'displayName').exec(function(err, posts) {
        if (err) {
            return res.send(400, {
                messages: getErrorMessage(err)
            });
        } else {
            res.jsonp(posts);
        }
    });
};

If you are wondering what Post is here, that is our Mongoose model that we first talked about when we added our custom blog fields.

So although there is code spread out over more files, MEAN style apps are legitimately more involved. And thankfully the generator did all the wiring work for us. Now that we have a little better idea how things are stitched together in MEAN, let’s continue improving our blog.

Improving on the Validation

Our working CRUD, navigation, and Posts schema, takes us to about half-way through the Rails guide. The next step here is to beef up the validation. We already validate the presence of our fields, the generator provided that in the schema def with the “required:” item. Now we need to add the rule to check for a minimum title length. Because the MEAN.js way of handling validation comes from the Mongoose schema, we just need to add a bit to the definition. I used a custom validate function like this:

title: {
        type: String,
        default: '',
        required: 'Please fill Post title',
        trim: true,
        validate : [
            function(v) { return v.length >= 4; },
            'Title must be at least 5 letters'] 
    }

With this in place, we have basic validation for the posts. When testing this out, I still found the display of the errors lacking: the view only displayed one validation issue at a time. I modified the generated code a bit to handle a list of errors, you can see the code in the next commit. The results of our new validation logic looks like this when we try to enter an invalid post:

Wrapping Up… for now

Most of the next steps in the Rails guide involve setting up the list and details views, which we already have thanks to the CRUD module generator. From there the guide adds comments to the posts. But I will stop here for this entry... I think we’ve accomplished enough at this point. So there we have it: we have basically followed in the footsteps of the demo that put Rails on the map, with the newer MEAN.js stack. Our demo also could be completed in only minutes, and we saw how similar the experience of starting a new app is to Rails.

Conclusion

It was an interesting, though short, journey to recreate the famous Rails blog in this new stack. I am now convinced that my friends are justified in their enthusiasm for AngularJS and MEAN: the stack might well be a successor to Rails for newer style web applications. The experience of creating an initial application on this stack was quite similar to Rails, and mostly as fun. The big difference to me was the missing ability to wire schema fields into generated code, which is something I imagine will still come. The MEAN stack also still holds the promise of more shared logic between client- and server-side, although I don’t think the current stack capitalizes on this nearly enough--another area for future investigation. After creating my own version of the famous Rails blog, I believe that AngularJS and the MEAN.js stack present a strong option for future web applications.

About the Author

John Troxel is an independent software developer and leader, currently working with the photo-sharing giant Flickr. John has written software for many years in many languages including Java, Ruby, and of course JavaScript. On occasion, he has also found himself managing various delivery groups, but he does not wear suits often. John is always looking to help clients beat up the competition with agile and rapid development.  When not working, he chases his 3 boys and sometimes tries to coach them in sports.

Rate this Article

Adoption Stage
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Nice article - a better comparison might Rails vs SailsJS by seldon richard

John, I liked this article. Still, you might have been better using SailsJS in order to perform the comparison. Think you'll find it like Rails on steroids with built in Socket IO support too.

Re: Nice article - a better comparison might Rails vs SailsJS by John Troxel

Thanks Seldon. Being newer to the node world, that is something that definitely warrants a closer look. Does Sails do any UI generation? That was sort of the essence of the original demo and the comparison to MEAN.js.

Meteor by brian dsouza

Mean is one example of a new paradigm and not 'the' new paradigm. In reviewing Mean would it make sense to discuss other instances such as Meteor?

Re: Meteor by John Troxel

Meteor is definitely cool and I am sure worth a look. I might consider similar posts to this for other frameworks if time allows. For this particular post, MEAN is what I had been hearing the most about, so I simply wanted to give it a try and compare the experience to Rails.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

4 Discuss

Login to InfoQ to interact with what matters most to you.


Recover your password...

Follow

Follow your favorite topics and editors

Quick overview of most important highlights in the industry and on the site.

Like

More signal, less noise

Build your own feed by choosing topics you want to read about and editors you want to hear from.

Notifications

Stay up-to-date

Set up your notifications and dont miss out on content that matters to you

BT