CHAPTER 6

image

Running TypeScript on a Server

If it seems strange to you that Node achieves parallelism by running only one piece of code at a time, that's because it is. It's an example of something I call a backwardism.

—Jim R. Wilson

Running JavaScript on a server is not a new concept—Netscape Enterprise Server offered this feature as long ago as 1994. There are currently a whole host of server-side implementations of JavaScript running on more than six different script engines. As well as these pure JavaScript implementations, JavaScript can also be run within other platforms that supply a script host.

Although the JavaScript language is common to all of these implementations, each one will supply different APIs for performing operations that are not usually available within a JavaScript program. The range of available modules within a server-side implementation is paramount, which is why Node has been such a great success and why it has been selected for this chapter.

Not only does Node have over 70,000 modules available from simple helpers to entire database servers, it is possible to add them to your program with one simple command thanks to the Node Package Manager (NPM). This means you can add a database module such as MongoDB simply by typing npm install mongodb into a command window. Node is cross platform and offers installers for Windows, Mac OSX, Linux, and SunOS as well as full access to the source code.

To demonstrate the use of Node within TypeScript, this chapter gradually evolves a simple application into one that uses several modules. This demonstrates the code as well as the process of adding packages and type definitions. Although the examples show screenshots from Visual Studio, NuGet, and Windows Command Prompt, you can easily transfer these to other development tools and operating systems, for example Sublime Text 2 and Terminal on OSX or WebStorm and the terminal on Linux. The combinations are many and varied and several integrated development environments are cross platform if you want a similar experience on different machines (Cloud9, Eclipse, Sublime Text 2, Vim, and WebStorm all run on Windows, OSX, and Linux).

Install Node

You can download the installer for your chosen platform from the NodeJS (http://nodejs.org, Joyent, n.d.) website

http://nodejs.org/download/

Creating a New Project

The example program will start from a completely empty project. Figure 6-1 shows the starting state of the example project and solution, which contains a single empty app.ts file.

9781430267911_Fig06-01.jpg

Figure 6-1. Empty TypeScript project

If you are using Visual Studio, you can replicate this by creating a new TypeScript HTML application and deleting all of the files except the app.ts file. If you are using a different development environment, you can simply start a new project or folder and add an empty app.ts file.

To get autocompletion and type checking for Node, you will need a type definition that describes the standard Node API. You can download an existing definition from the Definitely Typed project:

http://definitelytyped.org/

You also can download the definition using the NuGet package manager in Visual Studio. The Definitely Typed project has definitions for Node and many of the available modules. Figure 6-2 shows the NuGet package manager user interface, with the value node.TypeScript.DefinitelyTyped in the search field. Click “Install” to add the type definition to your project.

9781430267911_Fig06-02.jpg

Figure 6-2. Using NuGet to install Node type definitions

After installing the Definitely Typed NuGet package for Node, your project will match the image in Figure 6-3. All type definitions are added to the Scripts/typings directory in a folder that matches the library name.

9781430267911_Fig06-03.jpg

Figure 6-3. Newly added files

The final piece of setup to get your project working for Node is to change the project settings to use the CommonJS module system instead of the AMD module system, as shown in Figure 6-4. The AMD module system is for asynchronous module definitions (which is a common pattern to use in web applications) but CommonJS is the module loading pattern used by Node.

9781430267911_Fig06-04.jpg

Figure 6-4. Change project settings to use CommonJS

This setting is equivalent to passing the --module commonjs flag to the TypeScript compiler, as described in Appendix 2.

Simple Node Program

Now that the project is set up, it is possible to demonstrate Node using a simple program that runs a web server and responds to requests. The HTTP server simply passes all requests to a callback function that you supply. There is no built-in facility to handle different requests or routes or to help formatting a response (if you want these, they are available in middleware, such as Express, which is covered later in this chapter).

Listing 6-1 shows the complete program, which creates an http server listening on port 8080. All requests are passed to the requestListener function, which gives a standard text response to all requests. The requestListener function is passed two arguments representing the request and the response. Information can be obtained from the request parameter, such as the method, headers, and body of the request. You can add content to the head and body of the response parameter. You must indicate that you are finished by calling response.end(), otherwise the HTTP server sends no response to the client leaving the client waiting for a response until it times out.

Listing 6-1. A simple Node server

/// <reference path="scripts/typings/node/node.d.ts" />

import http = require('http'),

var portNumber = 8080;

function requestListener(request: http.ServerRequest, response: http.ServerResponse) {
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.write('Response Text Here'),
    response.end();
}

http.createServer(requestListener).listen(portNumber);

console.log('Listening on localhost:' + portNumber);

A reference comment is used to indicate the location of the Node type definitions. This allows the import statement to reference the http module, which is not present as an external module in the project—the http module will be supplied by Node at runtime.

To run the http server from this listing, open the command prompt from the project folder and run the command node app.js. You should see the message “Listening on localhost:8080” in the command window as shown in Figure 6-5. The server runs as long as the command window remains open.

9781430267911_Fig06-05.jpg

Figure 6-5. Running the program

To make a request to the server, open a web browser and enter localhost:8080 in the address bar. You should receive the “Response Text Here” message shown in Figure 6-6. Because all requests are sent to the same requestListener method, you can enter any address at localhost:8080 and receive the same message, for example localhost:8080/Some/Path/Here/ or localhost:8080/?some=query&string=here.

9781430267911_Fig06-06.jpg

Figure 6-6. Calling the program from a browser

Request Information

It is almost certain that you will want to obtain information from the request to provide a response that matches the requested information. Listing 6-2 shows how to obtain the request method and information about the requested URL, which could be used for routing a request or obtaining data to be used to find data matching the request.

Listing 6-2. Getting more information from the request

/// <reference path="scripts/typings/node/node.d.ts" />

import http = require('http'),

var portNumber = 8080;

function requestListener(request: http.ServerRequest, response: http.ServerResponse) {
    response.writeHead(200, { 'Content-Type': 'text/plain' });
    response.write('Method: ' + request.method + ' '),
    response.write('Url: ' + request.url + ' '),
    response.write('Response Text Here'),
    response.end();
}

http.createServer(requestListener).listen(portNumber);

console.log('Listening on localhost:' + portNumber);

In this example the information obtained from the request is simply appended to the response to show you the information in the browser when the request is made. The response is shown in Figure 6-7 based on entering http://localhost:8080/Customers/Smith/John into the address bar. You can use the properties of the request to decide how to handle the request.

9781430267911_Fig06-07.jpg

Figure 6-7. Displaying information about the request

Although you could use this information to write your own framework for servicing the requests made to your Node server, the work of routing requests and obtaining information from the request has been done elegantly already and is available as a module that you can install with NPM. Unless you want to use the request information to do something unusual, you may find that using an existing module that provides a framework for your program will save time and effort as well as covering scenarios not planned for.

The next section describes how to build an application using the Express module, a lightweight framework for Node applications that doesn’t dictate the details of authorization, persistence, or templating.

Using Express to Write Applications

Working with the raw request and response in Node allows access to the low level details of HTTP communication; but in most cases you won’t be interested in dealing with all of the details yourself. The Express module provides a framework that allows you to concentrate on your application rather than on routing and HTTP communication. Express is both a quick way to get started and a robust framework for bringing together your program.

Before you install Express, you should create a package.json file in your project folder. This file contains metadata for your project and also will be a source of information about your dependencies. An example package.json file is shown in Listing 6-3.

Listing 6-3. Example package.json

{
    "name": "Example",
    "description": "An example application using Node and Express",
    "version": "1.0.0",
    "author": {
        "name": "Steve Fenton",
        "url": "http://www.stevefenton.co.uk/"
    }
}

The name and version fields are required if you want to publish your package via NPM and are consistently described as being the most important fields in the file.

To install Express, run the command npm install express --save. This command downloads Express with all of its dependencies as shown in Figure 6-8. You also will see any errors or warnings generated by your package.json file, for example if you haven’t supplied a recommended property.

9781430267911_Fig06-08.jpg

Figure 6-8. Using Node Package Manager to install Express

After installing Express using NPM, you will notice that the package.json file has been automatically updated to include the dependency on Express, as shown in Listing 6-4. This is only done if you pass the --save flag to NPM.

Listing 6-4. Updated package.json

{
    "name": "Example",
    "description": "An example application using Node and Express",
    "version": "1.0.0",
    "author": {
        "name": "Steve Fenton",
        "url": "http://www.stevefenton.co.uk/"
    },
    "dependencies": {
        "express": "^4.1.1"
    }
}

To get autocompletion and type checking for Express, there is a type definition on Definitely Typed. Once again, you can use NuGet to download the type definition in Visual Studio by searching for express.TypeScript.DefinitelyTyped (shown in Figure 6-9), or you can download it manually from the Definitely Typed website.

9781430267911_Fig06-09.jpg

Figure 6-9. Using NuGet to install the Express type definitions

Simple Express Program

Listing 6-5 is an updated version of the simple Node program based on Express, rather than on the http module. Although the general pattern is similar, the requestListener is specifically added to the HTTP GET method at the root address of the application.

Listing 6-5. Using Express

/// <reference path="scripts/typings/express/express.d.ts" />

import express = require('express'),

var server = express();
var portNumber = 8080;

function requestListener(request: express.Request, response: express.Response) {
    response.send('You requested ' + request.query.firstname + ' ' + request.query.lastname);
}

server.get('/', requestListener);

server.listen(portNumber, () => {
    console.log('Listening on localhost:' + portNumber);
});

This means that the requestListener function will only be called for requests to http://localhost:8080/. Unlike the earlier examples, requests that don’t match the route will fail, for example a request to http://localhost:8080/Customers/Smith/John would receive a 404 response with the message “Cannot GET /Customers/Smith/John”.

A correct request to this example should include firstName and lastName query string arguments. Express maps the query string to the request.query property. The full example request address of http://localhost:8080/?firstname=John&lastname=Smith will result in the message “You requested John Smith” being returned in the response, as shown in Figure 6-10.

9781430267911_Fig06-10.jpg

Figure 6-10. Calling the Express program

Multiple Routes

You can provide different handlers for different routes in your program, for example the code in Listing 6-6 provides one handler for http://localhost:8080/ and another for http://localhost:8080/Test/. Express handles all of the routing for you and ensures the correct function handles each request.

Listing 6-6. Assigining handlers to routes

function handlerOne(request: express.Request, response: express.Response) {
    response.send('You got handlerOne'),
}

function handlerTwo(request: express.Request, response: express.Response) {
    response.send('You got handlerTwo'),
}

server.get('/', handlerOne);
server.get('/Test/', handlerTwo);

You can test these routes in a browser, just as before, and get the appropriate responses

  • http://localhost:8080/ -> “You got handlerOne”
  • http://localhost:8080/Test/ -> “You got handlerTwo”

Requests to routes that have not been registered will result in a 404 not found response.

Handling Errors

You can supply a general error handler for your application by supplying a function that accepts four arguments to the server.use method. In Listing 6-7 the handler function has been changed to throw a deliberate error. The error handler is set using the server.use method and logs errors to the console before returning a 500 response code.

Listing 6-7. General error handler

/// <reference path="scripts/typings/express/express.d.ts" />

import express = require('express'),

var server = express();
var portNumber = 8080;

function handler(request: express.Request, response: express.Response) {
    throw new Error('Deliberate Error!'),
}

server.get('/', handler);

server.use((error, request, response, next) => {
    console.error(error.stack);
    response.send(500, 'An error has occurred.'),
});

server.listen(portNumber, () => {
    console.log('Listening on localhost:' + portNumber);
});

When you make a request to the application, you should see the error details in the command window as shown in Figure 6-11 and in the browser you will see the message “An error has occurred”.

9781430267911_Fig06-11.jpg

Figure 6-11. The logged error

Express Book Project

Now that you have seen the basic elements of an Express application, it is worth looking at a more complete example that accepts requests, serves web pages, and stores data in a database. Although this section won’t cover every aspect of Express (a topic that warrants a book in its own right) it will cover what you need to get an application up and running, including accepting input from the user and storing data in a database.

If you are using Visual Studio, you can short-cut the entire process of setting up a new Express application using the NodeJS Tools project on Codeplex (https://nodejstools.codeplex.com, Microsoft, n.d.). The tools add a series of project templates to Visual Studio, including a “TypeScript->Node.js->Basic Express Application” template. You can download the tools from:

https://nodejstools.codeplex.com/

The Basic Express Application template provides a common structure for your Express application, although it is worth bearing in mind that the structure is not a requirement of Express itself, which is incredibly flexible. The template adds Stylus for CSS preprocessing and Jade for templating. If you would prefer to use LESS for CSS preprocessing or EJS for templating, the NodsJS Tools conveniently provide quick access to NPM, which simply replaces NuGet for Node projects; this allows you to download packages to replace the defaults. In particular, both Stylus and Jade provide incredibly terse syntax so you may prefer alternatives that sacrifice brevity for readability. In this section, the defaults have been used in the example project.

To create a new project, select the Basic Express Application template, which is organized under Templates image Other Languages image TypeScript image Node.js shown in Figure 6-12. It is important that you select this version of the template and not the similar template found under the JavaScript language. The TypeScript version comes out of the box with .ts files containing the code and with type definitions for the default components.

9781430267911_Fig06-12.jpg

Figure 6-12. The TypeScript Basic Express Application template

When the project has been created, you will be prompted to update the NPM packages listed in the packages.json file. Although the project template could have contained all of these files, downloading them at this stage ensures that you have the latest versions of each module.

The app.ts file that comes with the template is a more mature version of the simple Node program from earlier in the chapter. Listing 6-8 shows an example of the code; this sets up the application for logging, routing, and preprocessing.

Listing 6-8. The Express app.ts

import express = require('express'),
import routes = require('./routes/index'),
import user = require('./routes/user'),
import http = require('http'),
import path = require('path'),

var app = express();

// all environments
app.set('port', process.env.PORT || 3000);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade'),
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.json());
app.use(express.urlencoded());
app.use(express.methodOverride());
app.use(app.router);

// register the stylus middleware
import stylus = require('stylus'),
app.use(stylus.middleware(path.join(__dirname, 'public')));
app.use(express.static(path.join(__dirname, 'public')));

// development only
if ('development' == app.get('env')) {
    app.use(express.errorHandler());
}

// register routes
app.get('/', routes.index);
app.get('/users', user.list);

// run the server
http.createServer(app).listen(app.get('port'), function () {
    console.log('Express server listening on port ' + app.get('port'));
});

You will need to build the application before you run it to compile the TypeScript. Development environments with strong TypeScript support, such as Visual Studio, will do this automatically when you run the application from the integrated development environment (IDE). When the application runs you will see in the command window that the debugger is attached before the Express server starts and each time a request is made, it is logged to the same window (see Figure 6-13).

9781430267911_Fig06-13.jpg

Figure 6-13. The Express server output

Adding the Book Route

To manage a list of books, the application needs to support a /book address. To support a typical address you need to add two files and edit another. You will need to

  • Add a TypeScript file to the routes folder
  • Add a Jade template to the views folder
  • Update the wiring the in app.ts file to register the route

Listing 6-9 shows the book.ts file that will handle requests to the /book address. Like all request handlers, the list function has request and response parameters. The handler calls the response.render method on the result, passing in the view name and the model object that represents the data to be displayed.

Listing 6-9. The routes/book.ts file

import express = require('express'),

export function list(request: express.Request, response: express.Response) {
    response.render('book', { title: 'Books' });
};

For this handler to work there must be a view with the specified name in the views folder. Listing 6-10 shows the book.jade template that will render the data supplied by the request handler. The template re-uses the layout.jade file, which is the default layout for the application and renders the title from the model object.

Listing 6-10. The views/book.jade file

extends layout

block content
  h1= title
  p Welcome to the #{title} page.

To register this route in your application, you need to amend the app.ts file to add the import statement to reference the book.ts file and to add the route registration. Listing 6-11 shows the two additional lines needed to link the /book address to the book.ts file containing the request handler.

Listing 6-11. The additions to the app.ts file

import book = require('./routes/book'),

app.get('/book', book.list);

When you run your application and visit the /book address in a web browser, you should see a page displaying the message “Welcome to the Books page”. If you don’t get to the new page, check the command window to view any errors. The most common error is a misspelling, for example accidentally entering 'books' as the view name when it should be 'book'.

Collecting Data

The first step toward storing some data is to supply a form that allows the user to enter information. Listing 6-12 shows the updated book.jade template, which now has a form accepting a title and author as well as an optional ISBN identifier for the book.

Listing 6-12. Adding a form to the Jade view

extends layout

block content
  h1= title
  p Welcome to the #{title} page.
  p #{message}
  form(method='post')
    fieldset
        legend Add a Book
        div
            label Title *
                br
                input(type='text', name='book_title', required)
        div
            label Author *
                br
                input(type='text', name='author', required)
        div
            label ISBN
                br
                input(type='text', name='book_isbn', pattern='(?:(?=.{17}$)97[89][ -](?:[0-9]+[ -]){2}[0-9]+[ -][0-9]|97[89][0-9]{10}|(?=.{13}$)(?:[0-9]+[ -]){2}[0-9]+[ -][0-9Xx]|[0-9]{9}[0-9Xx])')
        div
            button Save

The HTML attributes are added to elements by appending them in parentheses to the element name. Each input’s type and name attributes are added in this way. The notable attribute in the listing is the pattern attribute on the ISBN input. The ISBN isn’t required, but if it is supplied it must match the pattern supplied in this attribute.

If you are worried about having to write patterns such as the monstrous one above that accepts the various formats of an ISBN, don’t worry as this one and many others can be found in the HTML5 Pattern Library at http://html5pattern.com/.

To process the form when it is submitted, a function that handles the form post must be added to the book.ts file in the routes directory. Listing 6-13 shows the updated file with the submit function. At this stage the function simply provides a message to the view to confirm that nothing has been saved because there is no database yet. The database will be added in the next section.

Listing 6-13. Adding a handler to the book.ts file

import express = require('express'),

export function list(request: express.Request, response: express.Response) {
    response.render('book', { title: 'Books' });
};

export function submit(request: express.Request, response: express.Response) {
    response.render('book', { title: 'Books', message: 'Book not yet saved!' });
}

To send the form post to the submit function, the route must be registered in the app.ts file. Listing 6-14 shows the updated routes, which has the new post route that will forward matching requests to be handled by the book.submit function.

Listing 6-14. The updated routes in the app.ts file

// register routes
app.get('/', routes.index);
app.get('/book', book.list);
app.post('/book', book.submit);

If you compile and run the updated application and visit the /book address, you should see the form that allows books to be added. You will only be able to submit the form in a modern browser if you supply both a title and an author. If you enter any value into the optional ISBN input, it must be a valid format, for example the ten-digit 0-932633-42-0 or the thirteen-digit 9780932633422.

When you successfully submit the form, you should see the message from the book.submit route, which says “Book not yet saved”. You will notice that other than this message, the page displayed is same as the book.list route; this is because the same view has been used for both at this stage.

Installing Mongoose

There are many options for storing data in your Node application. You can use the file system, a relational database such as MySQL, or a NOSQL database like MongoDB. In this example MongoDB will be combined with Mongoose (http://mongoosejs.com/, LearnBoost, n.d.) to supply the data access for the application. Mongoose can simplify the handling of validation, queries, and type casting among other things.

Before you can follow the code in this section, you will need to set up your database. To download MongoDB for your platform visit http://www.mongodb.org/.

Once you have installed MongoDB, it is best to move it from the installation directory (e.g., Program Files on Windows) into a c:mongodb directory.

MongoDB stores your data on the file system, so you need to set up a folder to be used for storage. By default, MongoDB looks for a c:datadb directory, so you should add this directory before you proceed. You can place the data in a different directory if you want to. You will also need to supply the path to MongoDB when you start the database server. For now, just add the default directory.

To start the MongoDB database server, run the code shown in Listing 6-15 in a command window.

Listing 6-15. Running the database server

C:mongodbinmongod

You should get a message saying that the server is “waiting for connection on port [number]”. The number shown in this message will be needed when you connect to the database from your application. If you get any errors, double-check that you have set up the c:datadb directory.

You can install Mongoose and MongoDB modules in your application using NPM. In Visual Studio you can right-click on the NPM node in Solution Explorer, search for Mongoose, and press the install button as shown in Figure 6-14. For an alternative, you can manually run npm install mongoose@"*" --save from a command prompt running in your project folder. NPM will download Mongoose and its dependencies, which includes MongoDB.

9781430267911_Fig06-14.jpg

Figure 6-14. Installing Mongoose and MongoDB

To get autocompletion and type checking, you should download the mongoose.d.ts file from the Definitely Typed project. If you get any errors, you also may need to update your node.d.ts definition.

https://github.com/borisyankov/DefinitelyTyped/tree/master/mongoose

You should now have everything you need ready to start saving your data. The next section is a walk-through of the changes to the Express Book Project to store and retrieve the books entered by the user.

Storing Data

To store the data received when the user submits a new book, the book.ts file in the routes directory must be changed to call the newly installed database.

Listing 6-16 shows the updated handlers, the list handler that displays the books from the database, and the submit handler that saves new submissions. The code to connect to the database is outside of the functions and is shared between them.  A more detailed walkthrough of all the changes is shown below.

Listing 6-16. The updated routes/book.ts file

import express = require('express'),
import mongoose = require('mongoose'),
declare var next: (error: any) => void;

mongoose.connect('mongodb://localhost:27017/books'),

var bookSchema = new mongoose.Schema({
    title: String, author: String, isbn: String
});

var Book = mongoose.model<any>('Book', bookSchema);

export function list(request: express.Request, response: express.Response) {
    Book.find({}, (err, res) => {
        if (err) return next(err);
        response.render('book', { 'title': 'Books', 'books': res });
    });
};

export function submit(request: express.Request, response: express.Response) {
    var newBook = new Book({
        title: request.body.book_title,
        author: request.body.author,
        isbn: request.body.book_isbn
    });

    newBook.save(function (err) {
        if (err) return next(err);
        response.redirect('/book'),
    });
}

The database connection is made using the mongoose.connect call. The connection string in the example uses port 27017; you should use the port number that was shown when you started the MongoDB server. When your application connects to the database, each connection will be logged to the mongod command window.

The bookSchema variable is assigned a new Mongoose schema. This schema defines the shape of the documents to be stored in a collection. Mongoose sets up the MongoDB collection for you and can handle default values and validation. The schema for books is set using title, author and isbn properties, which are all assigned the type String. The definition of schemas is strikingly similar to TypeScript type annotations. Because of the context of the statement, the TypeScript compiler is intelligent enough to realize that they are not type annotations; therefore it doesn’t use type erasure to remove them from the compiled output. The String type in question is not the String interface that backs the string type annotation in TypeScript but a mongoose.Schema.Types.String. If you accidentally use the lower-case string type, the compiler will give you a warning about your mistake.

The Book variable is assigned a model object that Mongoose creates. This saves you having to write your own implementation of a Book class. You can use this model to create new instances of books whenever you need one, just as if you had written your own class.

Although this has taken a few paragraphs of text to explain, it is worth revisiting the code listing to confirm that it is possible to connect to a database and set up the schema for the book data in three lines of code. This setup is used in both of the functions that handle requests.

The list function calls the Book.find method supplied by Mongoose for retrieving records. You can supply an object to the find method to be used to filter the results. The object can be a partial match for the book schema. For example, you could use { author: 'Robert C. Martin' } to retrieve all the books by Uncle Bob. In the example, the empty object indicates that you want all documents in the collection.

Because the query is executed asynchronously, you must also pass in a callback function that will be called once the query has completed. The code to send the response must be nested within the callback, otherwise the response will be sent before the query is finished. You also have the opportunity to handle errors in the callback. The books collection is added to the model object passed to the view.

Image Note  Although the example uses an empty object to query the collection and retrieve all books, this query will get slower as more and more books are added.

The submit function instantiates a new Book object with the data submitted by the user and then calls the save method that Mongoose provides. Once again the database call is asynchronous and a callback is executed when the action is complete. In the code listing, when the record is saved successfully the response is simply a redirection to the list action. Redirecting the request after a submission prevents the user from accidentally resubmitting the same data by refreshing their browser. The pattern of redirecting after a successful submission is named the Post Redirect Get pattern.

Now that the route handler is passing the book data to the view, the view can be updated to show the data. The additional markup to add a table is shown in Listing 6-17.

Listing 6-17. The Jade table template

    table
        thead
            tr
                th Title
                th Author
                th ISBN
        tbody
            if books
                each book in books
                    tr
                        td= book.title
                        td= book.author
                        td= book.isbn

The each loop in the Jade template repeats the nested output for each item in the books collection. The table cells are declared using the shorthand syntax (the element name followed by an =). This means that the data variables do not need to be enclosed in the usual #{} delimiters as before.

Jade’s each loop will handle an empty array but not an undefined value. The if statement before the each loop in the example prevents undefined values reaching the each loop.

You now have a fully functioning application that will save and display books.

Summary

JavaScript is no stranger to web servers and has gained huge traction thanks to Node and the many thousands of modules available via the Node Package Manager. As bigger programs are written to run on Node, the language features and tooling provided by TypeScript increase quickly in value. A great deal of time can be wasted on simple mistakes such as placing code dependent on an asynchronous call outside of the callback function or using string when you mean to use String, and TypeScript can help to avoid these common errors.

The Express framework is a fast way to get started on Node and will provide some familiarity to programmers that have worked with Sinatra (or Nancy in .NET). Even for those not familiar with this exact style of implementation, the separation of the route handlers, models, and views is likely to be recognizable. Using Express will boost your productivity compared to handling the lower level HTTP requests and responses in Node.

Mongoose fulfils a similar role for the database, providing many shortcuts that will boost your productivity. MongoDB is not particularly tricky if you want to drop down a level and handle the models and validation yourself by calling MongoDB directly to store and retrieve data.

Although this chapter has happily stuck with all of the defaults that ship with Express, you are not limited to using these defaults. If you want to replace a default in your TypeScript program, simply check that your development tools support the alternatives. For example, the Node Tools for Visual Studio ship with Jade support, but you could obtain an extension to add EJS support if you would rather use it for your templates.

Key Points

  • JavaScript has been running on web servers for over 20 years.
  • Node will happily run on any platform.
  • You can get type information for Node and many of the Node modules from the Definitely Typed project.
  • Express supplies a lightweight and flexible application framework that is easier to use than the lower level Node HTTP request and response.
  • Mongoose and MongoDB supply simple persistence with an asynchronous API.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.221.66.185