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.0NODE_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_VERSIONNodeJS 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!
Internal Presentation #2: NodeJS project with TypeScript
By Róbert Beretka
Internal Presentation #2: NodeJS project with TypeScript
Presentation for Email Channel Crew about NodeJS project setup with TypeScript and best practices.
- 555