Moving From Rails to Sails.js

I created a web service called Butai last year using Rails, then completly rewrite it using Sails.js. Here are some thought about both frameworks.

Versions

Note that these are not the lastest versions because I made the site last year, Rails 4 is out for months now and Sails 0.10 beta is almost done as well.

Started with Rails

I started with rails. It speeds up development amazingly, you only need to type few command lines and the whole functional site is ready. When Rails doesn’t provide the function you want, most of the time some gems got you covered. I built the sites using tons of gems, user system powered by Devise, image resize and upload using Paperclip, kaminari helps me to paginate, and more to go. I don’t need to understand how is password encrypted, or where is the session stored. Things just work.

Then why moving?

Because everything is sync. Butai is a web service that let people upload images, two at a time. Images need to be resized, then uploaded to s3.

While Paperclips helps me to handles image resizing, it is slow, and simply not clever enough. It stops the whole server from handling any request during these time consuming operations. This is an expected behaviour rather than a bug. People workaround this by switching from WEBrick to Unicorn, then open starts multiple process to handle requests.

While it seems to be the solution, it doesn’t feel right to me. The correct way to solve this problem is not starting more process, but to handle the operations more efficiently, for example resize and upload image in parallel instead of series, and uploading images doesn’t require much computation power, why not handle request at the time? There is EventMachine that should have solved the problem, but Paperclip doesn’t seems to utilize it. This isn’t just the problem of Paperclip, but the whole community, everything in Rails is sync, it makes everything simple, but bad performance. I host few websites on the server, over 70% of the resources is occupied by Ruby and Rails.

Since NodeJS is famous for its event driven design, I decided to give it a try.

Sails.js

It is designed to be a JS version of Rails, many aspects of them are similar, expect Sails doesn’t have so much ‘gem’. As a web framework, association is surprisingly missing in Sails (Supported in 0.10 beta), and the default rendering engine EJS is not a very good choice as well.

However, Sails has some interesting function that Rails couldn’t provide, such as WebSocket. Sails is built on Express, which integrates with Socket.io very well. The connection is automatically setup once you created the project, it is very easy to add real-time element to your website. I use it with plain JS, but its model should work very with framework like Facebook’s React.

Everything is async (except EJS)

Async in Node.js works very well for time consuming operations, the sites is now much more performant than before. Some people don’t like the async pattern because it causes callback hell, but I find it somehow manageable if you use Promise pattern and async correctly.

Except EJS. EJS doesn’t render in async, that isn’t a big problem because rendering isn’t that time consuming. The problem is it doesn’t have a async template API. In Sails, models are fetched like User.find({id:1},function(err, user){ ... }) instead of user = User.find(...). EJS doesn’t accept any Promise or function as argument, so you will have to fetch things, store them and pass to EJS when everything is done. It works, but not as straight forward as Rails.

Now what?

Both of Rails and Sails are good at something.

I would recommend Rails if your site doesn’t mind bad performance or don’t have to do some time consuming tasks. Building sites with Rails is really fast, gems do most of the heavy lifting for you, you only need to install them.

Sails’ community is much smaller than Rails’, you will need to spend more time to build your sites. There are packages for NodeJS and Express, but they won’t magically create your sites like Rails does. You will have to glue them together, I find this process fun because I get a chance to understand how things actually work (I didn’t know it is bcrypt when using Devise, until it use passport.js), but you may want to skip it if you are in a hurry.