Virgilio

I've been putting this of, but it's time to write something about Virgilio. Virgilio is a pretty opinionated framework for building Node.js applications at the centre of a number of projects I've been involved in. Now I realize that 'framework' is a bit of a dirty word in the Node.js community, I think partly because it implies this huge block of monolithic code. It makes me proud that this is not the case for Virgilio, which has started small and now, a year later, has proven to be very resilient to function creep.

So, what are Virgilio's opinions then? Well, for starters is uses Promises (the amazing bluebird to be precise). And nope, not getting into that debate. Also, Virgilio uses one global configuration object which is shared with all the modules you define. And lastly, well, it kind of takes over managing the structure of your application from the filesystem.

Why? Because moving a function to another file and than having to update paths in a dozen of files is a pain. Because I'm not sure sure I want to write modular code if it means half of my lines are require statements. Because I don't care where my function lives. I just want to call it.

To me, it feels like Virgilio takes the same approach to managing functions that Gmail took to managing email: Before Gmail, I had this beautiful folder structure that once in a while I rebuilt completely because of some mails I could not fit in. Since I have Gmail I don't need that structure anymore. When I'm looking for a particular email I just use search. It's easier. With Virgilio it's the same thing. I can just call my function function and let Virgilio worry about where to find it.

In Virgilio you have to register your functions by passing them to a .defineAction$() call. This offer a number of interesting benefits, one of which is that each function can automatically be wrapped in bluebirds Promise.method() call. This ensures that when you call an action it will always return a promise, even if the function you defined returns an ordinary value or throws an error. It's a wonderful guarantee that takes away a source of confusion and bugs without forcing you to add a lot of boiler plate to simple synchronous code.

Another cool advantage of this approach, is that it allows you to easily extend a function with additional behaviour. For instance, suppose that you want to add memoization to a number of actions. Just write a plugin that hooks into the .defineAction$() call and adds it to any registration which contains a certain flag. Done.

One last example I'd like to give is the possibility to give each action a personalized context. For instance, Virgilio creates a bunyan child logger for each action, which adds the path of an action (it's namespaces and name) to each log entry. That's a piece of context you won't have to add to every single log call.

So that's Virgilio in a nutshell. Check it out on Github and let me know what you think!