Saturday, December 22, 2012

TypeScript Node.js Development Part 2 - Standard Packages and Azure Deployment

In Part 1 I went through the process of making a basic Node.js app using TypeScript in Visual Studio. In this post, I'll make the app a bit less basic, and deploy it to Azure (a.k.a. "THE CLOUD").

To make this server a bit easier to work with, I'll use three Node.js packages - express for routing etc, ejs for templating, and jade for confusing-looking HTML that seems like it's an interesting thing to try out. Since we're using Node.js, adding these packages is easy, just create a package.json file alongside your app.ts file:
    "name": "package-name"
  , "version": "0.0.1"
  , "private": true
  , "dependencies": {
      "express": "2.5.8"
    , "ejs": ">= 0.5.0"
    , "jade": ">= 0.0.1"

Then run "npm install" in the app folder.

This pulls in the packages, but TypeScript doesn't know how the express code is defined until we use the definition provided by node-definitions (which we covered in part 1). You could just add "/// <reference path="./node-definitions/express.d.ts" />" to the top of your app.ts file, along with the node.d.ts reference, but a nicer way of doing this is to separate all the definition references to another file, app.d.ts:
/// <reference path="./node-definitions/node.d.ts" />
/// <reference path="./node-definitions/express.d.ts" />
And reference "app.d.ts" at the top of all your TypeScript files. This way, you can add references to your app.d.ts file as required, and the definitions will be available in all your source code files.

I won't go into details about express, ejs, or jade, firstly because I don't really understand it in-depth yet, and secondly because I think the code is pretty self-explanatory. The new app.ts, with some sample get and post URLs, looks like this:
///<reference path='app.d.ts' />

import http = module("http")
import url = module("url")
import routes = module("./routes/index")
import express = module("express")

var app = express.createServer();
var port = process.env.PORT || 1337;

// Configuration
  app.set('views', __dirname + '/views');
  app.set('view engine', 'jade');
  app.use(express.static(__dirname + '/public'));

app.configure('development', function(){
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));

app.configure('production', function(){

// Routes

app.get('/', routes.index);

app.get('/testUrl', function(req, res) {
    console.log('test url ' + req.query['testQS']);
    res.send('ok', 200);

app.get('/testUrl/:folder', function(req, res) {
    console.log('testing folder ' + req.params.folder);
    res.send('ok', 200);
});'/testPost/:userid/newboard', function(req, res) {
    console.log('testing post ' + req.params.userid + ', ' + req.param('postdata'));

app.listen(port, function(){
    console.log("Express server listening on port %d in %s mode", port, app.settings.env);

export var App = app;

We also have a separated route file in routes/index.ts - using routes specified inline or in external files is a matter of preference but I've included both just so you can see how it works:
///<reference path='app.d.ts' />
import express = module("express")
export function index(req: express.ExpressServerRequest, res: express.ExpressServerResponse){
    res.render('index', { title: 'Page Title', testArray: ["1", "2", "3", "4"] })

And our jade "HTML" looks like this:

h1= title
p Welcome to #{title}

- each test in testArray
    a(href= "/user/"+test)
      b= test

    title PageTitle
    link(rel='stylesheet', href='/css/style.css')
    #container!= body

Finally we have some basic css (public/css/style.css) that I must have gotten from some example code somewhere:
html { background-color: #f9f9f9; margin: 0; padding: 0; }
body { margin: 0 auto; padding: 0; font-family:"Segoe UI","HelveticaNeue-Light", sans-serif; font-weight:200;}
h1, h2, p, summary, footer, li { line-height: 170%; }
h1, h2 { border-bottom: 1px solid #aaa; font-family:"Segoe UI Light","HelveticaNeue-UltraLight", sans-serif; font-weight:100; }
h2 { font-size: 16pt; }
p { margin: 1em 20px 0 20px; }
ul { margin-top: 1em; }
#footer { font-style: italic; color: #999; text-align: center; padding: 1em 0 2em 0; margin-top: 1em; font-size: 80%; }
em { letter-spacing: 1px; }
li { margin-left: 1em; }

After all of this code, you should now be able to build the project and run "node app", then test out the various URLs and check out how the jade template is translated to HTML.

Now we can move onto something pretty awesome, deploying this code to Azure - which has a free tier that allows you to run up to 10 web sites for free. The Azure team has a really good tutorial that goes through setting up a new Azure Node.js website and getting it ready for git deployment, and they describe the process a lot better that I can, so go ahead and read through that, but instead of using their sample Node.js code, use the code that we have just written.

If you're back, hopefully you've managed to deploy this code to Azure, if so well done! The only thing I want to add is that if you're like me you don't like the idea of publishing a whole lot of files that are not needed for the web server. To avoid this, you can make a .gitignore file that includes things like the node definitions and useless dll files, as well as local copies of the node modules (since Azure will automatically npm install your app once deployed):

So there you have it, Node.js code, written in TypeScript, built using Visual Basic, running on Azure. This post was mainly just code or links and not much instructions, but hopefully it is useful for someone. Since this is starting to involve a bit of code, I've made a Visual Studio template so you can quickly make a new project with everything set up for you. However, things like node-definitions and possibly other packages will be a bit out of date and may need to be updated.

The next part will cover getting a basic front-end framework up and running.

Previously: TypeScript Node.js Development Part 1 - Getting Started, next: TypeScript Node.js Development Part 3 - Twitter Bootstrap

No comments:

Post a Comment