HomeBlogTexas Ranger

Software-Development, the final frontier, JavaScript has gone where no Java-man has gone before.
Looking at various statistics on programming languages, such as the Popularity of Programming Language Index or the TIOBE Index, we can clearly see JavaScript's rise. It has been the language for rich HTML-based internet applications for a long time, and since the creation of Node.js, it's on the server, too. It has become not only a language for server-side apps but also a general purpose scripting language. The only drawback is it's dynamic typing. Oh, I can hear you, you JavaScript purists! "One does not need static typing for writing good code", "static typing makes things more complicated", "I'm such a guru and need all the dynamic capabilities for my ingenious algorithms", "I have a lot of unit tests!". To be honest, I don't want to get into that discussion. If you don't appreciate the dozen advantages of static typing, this ain't the right blog four you. :-) TypeScript adds static typing to JavaScript without adding too much ALM-overhead. You keep the transpiler running in the background (in watch-mode) and it will transpile on every file-save. And what many people tend to overlook: If you use an HTML 5 templating engine (such as React-JSX) which translates the view templates to JavaScript statements, TypeScript will give you the tremendous advantage of statically typed views. This is what I have dreamed of since my JSP (custom tags) and ASP and ASP.NET days (only as far as software is concerned...): Have you ever pulled your hair because some of your 150 views (forms, pages) throw runtime-errors everytime you refactor your domain model or the view model? This is because "ordinary" views in "ordinary" templating engines are string-based. With TypeScript + JSX this can never happen again: the transpiler will uncover each an every wrong property or attribute.

I want it simpler and easier!

So all that said: We love TypeScript and the static typing approach for apps. But wait a minute: We talked about server-side JavaScript a minute ago, so why not use TypeScript on the server/backend, too? And by doing so, we create a bunch of new opportunities:

  • Use the same domain model on the client and on the backened.
  • Use the same components/modules on the client and the backend: Logging, date-arithmetics, formatting, http, reflection/matadata, mathematical functions and many more.
  • Use the same ALM-mechanisms on the client and the server: Language, transpiler, test cases/functions/mocks, package manager, source-control, integration and deployment.
  • Train new team-members only in one language and ALM.

This is how it might look like:

"Commonly used modules"



The client and the backend share a module called mrutils (Logging, date-arithmetics, formatting, ...). Of course we need to take care that they do not have any dependencies to client/server-specific code and in some cases we can use polyfills. That's already quite nice: All we need is a source-repository such as git and the NPM package manager. But how can we reuse NPM-modules on the client? We will see that in the second part of the post but mind the spoiler: WebPack does the trick. Let's take a close look at the Node.js modules system in order to understand the basic idea.

The Node.js module system from 10,000 feet

Node.js uses folder-based modules: Each Node-App keeps the modules it depends on in a folder node_modules. Well, there are globally installed modules but we won't cover them here.

This is how it looks like in Visual Studio Code:

"node_modules"



A node module or application is described in a file called package.json. It contains all the information necessary to build the module or app: The dependencies, the dev-time-dependencies (e.g. tools like TypeScript), a link to the source-repo, scripts for building or launching the app and much more.

This is how it might look like:

{
    "name": "cohera",
    "version": "0.0.22",
    "description": "enterprise relationship management",
    "main": "./js/server.js",
    "engines": {
      "node": ">=8.1.0",
      "npm": ">=5.2.0"
    },
    "scripts": {
      "startlocal": "tph app-server && nodemon ./js/server.js",
      "deploy": "git push"
    },
    "repository": {
      "type": "git",
      "url": "..."
    },
    "author": "thomas",
    "license": "MIT",
    "dependencies": {
      "express": "^4.15.3",
      "body-parser": "^1.17.2",
      "busboy": "^0.2.14",
      "cors": "^2.8.3",
      "fs-extra": "^3.0.1"
    },
    "devDependencies": {
      "@types/node": "^8.0.14",
      "@types/body-parser": "^1.16.4",
      "@types/busboy": "^0.2.3",
      "@types/cors": "^2.8.1",
      "@types/express": "^4.0.35",
      "@types/fs-extra": "^3.0.2",
      "concurrently": "^3.4.0",
      "cpx": "^1.5.0",
      "nodemon": "^1.11.0",
      "typescript": "^2.3.2"
    }
  }

This a Node.js Express application named Cohera (a system for enterprise relationship management). Express is a lightweight HTTP-server that serves REST-responses. Under dependencies, we can see the modules we need for handling HTTP/REST-requests:

  • express itself
  • a body parser for parsing json-request-bodies
  • a streaming parser for multipart file uploads
  • a cross origin requests handler
  • file-system functions

The devDependencies contain the dev-tools, most important the TypeScript transpiler. The "scoped" module dependencies @types/... will install the TypeScript definition files. As the name suggests, they contain the type descriptions which will be used by the TypeScript transpiler to do the static type checking. For instance: @types\node contains all the type information for Node.js. Or in other words: We can use all Node.js functionality in a statically typed manner. Nice!

Creating a Node.js module from a bunch of TypeScript files

The entry point to every Node.js module is index.js. It exports references to each JavaScript file it contains. The index.js for the "commonly used functionality" module we saw in the image above (mrutils) looks like this:

"use strict";
function __export(m) {
    for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./dist/remoteBrowserServer"));
__export(require("./dist/shortHash"));
__export(require("./dist/logging"));
__export(require("./dist/rumble"));
__export(require("./dist/utils"));
__export(require("./dist/format"));
__export(require("./dist/rumble"));
__export(require("./dist/assertions"));
__export(require("./dist/code-annotations"));
...

As you can see, it imports (require) and re-exports various JS files from the dist directory. But how are are the JS generated from TypeScript? Let's have a look at the tsconfig.json for our module mrutils:

{
  "compilerOptions": {
    "outDir": "dist",
    "noEmitOnError": true,
    "noImplicitAny": true,
    "removeComments": false,
    "sourceMap": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "declaration": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "strictNullChecks": true
  },
  "lib": [
    "dom",
    "es2015"
  ],
  "exclude": [
    "node_modules"
  ]
  ...
}

The first compiler options sets the transpiler's output dir to dist. Et violĂ : the JS files end up in the dist folder and index.js imports an re-exports them. In order to support TypeScript static typing for the consumers of the module, we have to create an index.d.ts file, too:

export * from "./rumble/remoteBrowserServer";
export * from "./rumble/shortHash";
export * from "./rumble/code";
export * from "./rumble/format";
export * from "./rumble/logging";
export * from "./rumble/utils";
export * from "./rumble/rumble";
export * from "./rumble/assertions";
export * from "./rumble/code-annotations";

What does that mean exactly? Our TS-code-files are located in the folder rumble. And we do the same as we did with the JS files: We import and re-export the TS-files.

Using a Node.js module from TypeScript

TypeScript - much like ECMAScript (>= 2015) - supports the import keyword for using/importing code from other files or other Node.js modules. It's quite simple to import functionality from our mrutils module, e.g. in our Cohera app above.

import { ILogger, LogLevel, RemoteConsole, shortHash, Rumble, Junkie } from "mrutils"; 

This snippet finally reveals the benefit of Node.js modules for TypeScript: We import all the classes, interfaces and functions by a single import statement.

Application Lifecycle: How to manage my "private" Node.js modules?

So far we have seen how to create a Node.js module in TypeScript and how to import its functionality, but how do we handle the lifecycle of the module? How do we distribute it to our fellow developers? We could package and upload it to the npm registry (our a private version of the registry) and our fellow developers would simply add a dependency to their package.jsons. That would work fine, but it's cumbersome: Let's assume, we develop our app and various modules like mrutils in parallel. We keep switching between editors/IDEs/solutions for our app and for mrutils files. If we used the npm registry, we would have to package and upload our module on each change. But that's boring. We want to be able to reflect all changes to mrutils in our app immediately. Each time I change the format.ts file, e.g., I want to go back to my app and use it immediately.

To make it clearer, let's outline the local development folders:

dev
  cohera  [our application server files]
    ts    [source]
    js    [transpiled]
  mrutils [our resusable module]
    ts    [source]
    dist  [transpiled]

Luckily, Node.js resp. npm provides a nice tool for exactly that: npm link. It creates a symbolic link to a folder. So from our application perspective it looks like a npm module within the node_modules folder.

Example of creating a link (on a windows command shell):

c:\dev\cohera> npm link ..\mrutils 

From this moment on, the node_modules folder in our Cohera app looks like this:

"Symbolic link in node_modules"

Some Background on the Transpiler: Keep it running in the Background

Each folder (Cohera and mrtuils) is a TypeScript project on its own, i.e. it has a tsconfig.json of its own. The easiest way to provide that "immediate use", is to let the TypeScript transpiler run in watch mode:

c:\dev\cohera> tsc -w 
c:\dev\mrutils> tsc -w 

So thats it: No matter if we change code in the mrutils module or in our app, it will get transpiled immediatley.

Ebony and Ivory: Node.js and TypeScript

TypeScript and Node.js go together very well. Node.js benefits from TypeScript's static typing and TypeScript from the Node.js module and eco system. The dev-cycle is extremly simple and efficient: An editor, a source-repo, NPM and that's it.

In part two we will deploy our Node.js app to azure and shed some light on the azure IIS-Node-magic as well as the advanced azure developer tools (Kudu).