A .NET developer's jump into node.js with express + vash + sqlite

Summary

One of my favorite quotes found in the book The Pragmatic Programmer, Tip #8, "Invest Regularly in Your Knowledge Portfolio", pops into my head every so often when I'm bumming around:

Learn at least one new language every year. Different languages solve the same problems in different ways. By learning several different approaches, you can help broaden your thinking and avoid getting stuck in a rut. Additionally, learning many languages is far easier now, thanks to the wealth of freely available software on the Internet.

Learning a new language each year will never happen for me, but I've broadened the quote to include frameworks and other layers of the stack which is much more attainable, and extremely relevant to what I'm doing.

I've decided to learn node.js, which isn't a very big step for me since I'm a full stack developer anyway, and I do love me some javascript. As with any new language the easiest way for me to really learn it is to do a full blown project, so I've chosen this blog to be my first node app (oh god).

Visual Studio

Being a .NET developer and fighting with/being let down by various IDEs I've come to the conclusion that I can't live without Visual Studio; luckily there is a node plugin called nodejstools[1] in beta that seems to work well enough.

Nodejstools comes with pre-built projects, so jumping right into a Basic Express Application is incredibly easy. One of the first things I did was change out the Jade templating language for vash. I don't feel like learning the syntax of Jade, and vash[2] offers the familiar Razor view engine experience.

Installing vash is simple via the nodejstools npm manager. Search for "vash" and click install, ta-da. Next step is to switch out the express view engine by changing app.set("view engine", "jade"); to app.set("view engine", "vash");. In the views folder the .jade files are removed and I create the layout.vash and index.vash files.

layout.vash

<!doctype html>
<html>
    <head>
        <title>@@model.title</title>
    </head>
    <body>
        @@html.block("body")
    </body>
</html>

index.vash

html.extend("layout", function(model)
{
    html.block("body", function(model)
    {
        <h1>@@model.body</h1>
    });
});

For the Razor aware, you'll probably see some familiar concepts with the @@html.block acting as the RenderBody/RenderPartial methods. With this simple setup we can copy the index.vash file for our other views to display within the layout.

While coding the basic boilerplate HTML/vash I noticed I had lost syntax highlighting and formatting, doh! In Visual Studio (I could not for the life of me get this to work in WebStorm) I can associate the .vash files to the "HTML (Web forms) Editor" editing experience and get HTML highlighting again.

vash extension

Routing

Next item to tackle was routing. The express app creates an index route in app.js app.get("/", routes.index);. After creating a couple more routes I could see how app.js could start filling up with unnecessary calls. I found what I think so far is a cleaner way of handling routes by abstracting the http verb calls into the route files.

In app.js I removed var routes = require("./routes"); and app.get("/", routes.index);. Directly under app.use(app.router) I add require("./routes")(app); and replace everything in routes/index.js with the following:

module.exports = function ( app )
{
    app.get("/", function ( req, res )
    {
        res.render("index", { title: "Time for some dreets you guys.", body: ":D" });
    });
});

If all goes well when running the site again you should see ":D" in the view pane, and see "Time for some dreets you guys" in the page title.

To add another route such as /about we can add another line require("./routes/about")(app);, create routes/about.js, and place in the following code:

module.exports = function ( app )
{
    app.get( "/about-me", function ( req, res )
    {
        res.render( "aboutme", {
        themeClass: "none",
        title: "aboutme"
    });
});

Working with data

I decided to try and work in a repository pattern with a service (damn right we're going to over engineer this, enterprise4lyfe). I have a PostService, a base Repository that handles the basic sqlite commands, and a specific PostRepository the handles all requests pertaining to blog posts.

The PostService has two methods, getLatest(callback), and getByUrl(url, callback) for displaying posts.

var PostRepository = require( "../data/PostRepository" );
var PostService = function ()
{
    var repo = new PostRepository();
    return {
        getLatest: function ( callback )
        {
            repo.getLatest( function ( err, post )
            {
                callback( err, post );
            });
        },
        getByUrl: function ( url, callback )
        {
            repo.getByUrl( url, function ( err, post )
            {
                callback( err, post );
            });
        }
    }
};
module.exports = PostService;

For this project I decided to go with sqlite as it doesn't require any installation, and is one of, if not the fastest[3] relational database platform for small projects like this. I abstracted out basic commands to make it easier to switch out my database incase this provider didn't work as I expected, and to make it easier to refactor when I figure out how unit testing works in node.

var fs = require( "fs" );
var sqlite3 = require( "sqlite3" );
var Repository = function ( callback )
{
    var database_name = "./data/upnxt.db";
    var dbInstance = null;
    fs.exists( database_name, function ( exists )
    {
        if ( !exists )
            throw ( "Database not found" );
    });
    function using( callback )
    {
        dbInstance = new sqlite3.cached.Database( database_name );
        callback( dbInstance );
    };
    return {
        all: function ( query, callback )
        {
            using( function ( db )
            {
                db.all( query, callback );
            });
        },
        get: function ( query, data, callback )
        {
            using( function ( db )
            {
                db.get( query, data, callback );
            });
        }
    }
};
module.exports = Repository;

For lack of a better name, I created a using function to new up a database instance for the current call. When I'm more familiar with node I'd like to turn it into a per http request instance.

The heavy lifter in my data access is the PostRepository. This repo hands off data specifically manipulated for each service call. For me, this helps keep my data flow organized and precise. By using the base repository directly in my service, or calling a database directly in my service I open myself up for over-sized objects that are harder to manage, and makes my life miserable when it's time to figure out how unit tests work.

The PostRepository is a little long to post in its entirety, but the basic concept hopefully comes across in the code snippet below.

getByUrl: function ( url, callback )
{
    post = { next: null, prev: null, references: [] };
    repo.all( 
        "SELECT "+
        "P.Id, P.Url, P.Title, P.Body, P.LinkText, P.MetaTitle, P.MetaKeywords, P.MetaDescription, P.CreatedOn, P.GithubLink, "+
        "PR.Reference "+
        "FROM POSTS P "+
        "LEFT OUTER JOIN PostReferences PR ON P.Id = PR.PostId "+
        "WHERE "+
        "P.IsPublished = 1 "+
        "AND P.Url = $url "+
        "ORDER BY P.CreatedOn DESC", { $url: url }, function ( err, rows )
    {
            if ( rows && rows.length > 0 )
            {
                buildPost( rows[0] );
                buildReferences( rows );
                getNextPrev( repo, url, function ()
                {
                    callback( null, post );
                });
            }
    });
}

I use node-sqlite3's .all method in this instance due to there being multiple rows returned because of my the join. In the callback the buildPost function maps my entity to the post object I'm returning, and then builds out the references array. The getNextPrev function is passed the repository instance and I find the next and previous blog posts to create navigation near the page footer.

So far I haven't found a great way with sqlite to do next/prev records, so unfortunately I'm making three database calls on each page load. Lucky for me I'll probably never have more than one person on the site at a time, and I'll probably have less than 100 records, so I'll throw optimization to the wind for now.

The Leftovers

The last basic necessities for my site is error pages. Using the same method as earlier with the routes, but after app.use(express.static(path.join(__dirname, 'public'))); I insert require('./routes/error')(app);. This needs to come after the public static path otherwise this route will be riddled with asset calls (image & styles) since no path is provided.

In routes/error.js the follow code will route the request to views/404.vash or views/500.vash.

module.exports = function ( app )
{
    app.use( function ( req, res )
    {
        res.status( 400 );
        res.render( "404.vash", {});
    });

    app.use( function ( error, req, res, next )
    {
        res.status( 500 );
        res.render( "500.vash", {});
    });
}

Each view has static content so for now I'm passing an empty object.

Conclusion

My experience with node so far has gone alright. I have concerns about callback chaining and the impact it will have on refactoring. I feel like I'm putting more effort than I should into doing simple tasks, writing myself into a giant plate of spaghetti code. I'll chalk up my experiences thus far to inexperience.

The nodejstools plugin for Visual Studio has a ways to go. I had stability issues with Visual Studio, NPM manager would seemingly randomly stop working until I restarted Visual Studio, and the package manager no longer gives me search results. It is in beta so I'll give it some time, but I'll probably end up moving to Sublime or WebStorm for node development.

I'm going to take the post topic a bit further with a search provider and archive section so we'll see how it goes from here. You can view this project as a whole in my blog's github account, along with any other code snippets my posts contain in the future.