Internal Presentation #2
NodeJS Project

with TypeScript

Motivations

 

knowledge sharing for next project

not many good example in company repositories
 

work on best practices together
 

clean code  ->  cleaner tests  ->  cleanest project

NVM (even if Docker)

Node Version Manager

> brew install nvm

> cat ~/.bash_profile

export NVM_DIR=~/.nvm
source $(brew --prefix nvm)/nvm.sh

> command -v nvm
> nvm --version

0.33.11

> cat ~/.nvmrc

v10.0.0
NODE_VERSION=$(cat .nvmrc)

nvm cache clear
nvm install --latest-npm --lts $NODE_VERSION
nvm alias default $NODE_VERSION
nvm alias stable $NODE_VERSION
nvm use $NODE_VERSION

NodeJS v10 (LTS)

long-term support all the way to ~2020

EcmaScript 2018 draft features support

asynchronous iteration and generators

rest/spread properties

Promise.finally

experimental EcmaScript modules instead of CommonJS

Proper use of NPM

task managers (gulp) mostly unnecessary - use npm scripts

npm configuration for project through .npmrc

no global dependencies, use ./node_modules/.bin through scripts

# .npmrc

engine-strict     = true     # strict engine check
node-version      = v10.0.0  # node version for strict engine check
save              = true     # update package.json after install/update
save-exact        = true     # update package.json with the given version
package-lock      = true     # create and update package-lock.json
# "scripts"

"initialize": "npm doctor && npm prune && npm cache verify && npm dedupe && npm ci",

Docker - Makefile

development - build - production ready usage through docker
also working without docker through npm scripts

Makefile

Docker

npm scripts

.nvmrc

Continuous Integration

Codeship namespace is global, blocking builds, maybe Heroku CI?
.slugignore for Heroku (only useful assets deployed)

Security Checking

for proper dependency management

# "scripts"

"security:check": "nsp check --reporter minimal",

# .nsprc

{
  "exceptions": [
    "https://nodesecurity.io/advisories/566",
    "https://nodesecurity.io/advisories/525"
  ]
}

# from NPM v6

"security:check": "npm audit",

TypeScript pros

 

not only a different language, but a tool
 

not the second CoffeeScript, 6 years history, increasing support
 

advanced type checking and definitions, also type inference
 

ES2018 target at the corner

any types, strange errors -> well-defined types, useful output

TypeScript contras

 

@types/* declaration packages are not unified quality

must keep @types/* packages sync with real ones

missing @types/* packages :(

But, easy to generate type declartions for packages now

{
  "compilerOptions": {

    "declaration": true,
    "declarationDir": "./dist",

  }
}

TypeScript in production

tslib

reflect

sourcemap

compilation

TypeScript configs

for different purposes

# tsconfig.base.json
# tsconfig.test.json
# tsconfig.build.json
# tsconfig.json

{
  "compilerOptions": {

    "noFallthroughCasesInSwitch": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,

    "strict": true,
    "strictFunctionTypes": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,

  }
}

"watch": "rm -rf dist && tsc --project ./tsconfig.build.json --watch",
"build": "rm -rf dist && tsc --project ./tsconfig.build.json",

Koa Action in TypeScript

import { ApplicationConfig, QueryParameters, ActionResponse, Logger, ActionParameters } from '../../../modules';
import { authDatabaseMiddleware } from '../../../middlewares';
import { ActionDescription } from '../../../decorators';
import { BaseAction } from '../../../models';

@ActionDescription({
  method: 'GET',
  path: '/api/accounts',
  middlewares: [authenticationMiddleware],
})
export class AccountsAction implements BaseAction {

  private _config: ApplicationConfig;
  private _query: QueryParameters;
  private _response: ActionResponse;
  private _logger: Logger;

  constructor({ config, query, response, logger }: ActionParameters) {
    this._config = config;
    this._query = query;
    this._response = response;
    this._logger = logger;
  }

  public async execute(): Promise<void> {
    this._logger.success({ message: 'Ok' });
    this._response.respondWithOk();
  }

}

Code Style checking

 

more deeper and stricter style checks because of TypeScript

TSLint is the "TypeScript-version" of ESLint

"code:style": "tslint --config ./tslint.json --format stylish --project ./tsconfig.json",

{
  "rules": {

    "no-unnecessary-type-assertion": [true],

    "await-promise": [true],

    "strict-boolean-expressions": [true],

  }
}

Test framework

Jasmine vs Tape vs Mocha
 

Mocha is the most configurable and reliable

Mocha has async support, helper modules preload, leak detection, etc

# .mocharc

--no-colors
--reporter spec
--require ts-node/register
--slow 100
--timeout 5000
--ui bdd
--check-leaks
--exit
--retries 5
--use_strict

Integration tests

easy API tests with supervisor

catch outgoing requests with nock


separate execution for integration and unit tests

describe(integration('Register Action'), () => {

  context('when authentication cookie not posted', () => {

    it('should respond with unauthorized', async () => {
      return request(startApplication())
        .post('/api/register')
        .send({ user: 'stoiet' })
        .expect(UNAUTHORIZED);
    });

  });

});
describe(integration('Get Action'), () => {

  it('should respond', context(({ suiteBackend }) => {
    suiteBackend
      .whenGET({ url: `campaigns/${CAMPAIGN_ID}` })
      .thenRespondsWith({ subject: 'subject' });
  }));

});

@emartech/* packages

 

boar-server - not well maintained, really old
 

boar-koa-server - younger one, really thin layer
 

json-logger-js - not bad

boar-tasks/boar-tasks-server - horrible

Extra Tooling

 

AutoCannon - HTTP load benchmarking tool for NodeJS

Node-Clinic - diagnose NodeJS performance

0x - profiling tool for NodeJS

Thanks!

Made with Slides.com