This is a step by step instruction for creating Node.JS web server application with typescript and express. Creating of new web application on Node.JS is is not making often, so the steps how to do it can be forgotten. So this article is a kind of reminder for me and other developers. Also I added in article links to video clips for more clarification. Here I show how to create application on Node.JS without using any frameworks for it. Just Node.JS and packages.
So, step by step:
So, step by step:
- Simple Web application youtu.be/7MIIcFDeSg4
Install in right order Node.JS, packages and configure them.
1) node.js download, 2) Create directory for your project, 3) npm init, 4) in package.json "main": "dist/index.js", "scripts": { "build": "tsc", "start": "node ." } 5) npm install --save-dev typescript, 6) in tsconfig.json { "compilerOptions": { "esModuleInterop": true, "outDir": "dist", "baseUrl": "." } } 8) npm install express, 9) npm install @types/express, 10) create src folder, 11) create src/index.ts with code below: import express from 'express' const app = express(); const port = 5000; app.get('/', (request, response) => { response.send('Hello world!'); }); app.listen(port, () => console.log(`Running on port ${port}`)); 13) npm run build, 14) npm run start, 15) localhost:5000
- Debug and initialization in Node.js youtu.be/hfST0e1ITGw
Set up debug mode and create an .env file to set input values.
1) in tsconfig.json add: "sourceMap": true 2) int package.json add: "prestart": "npm run build", 3) In IntelliJ IDEA in Run/Debug Configurations choose: "npm" and add script 4) npm i ts-node-dev --save-dev 5) int package.json add: "server:watch": "ts-node-dev --respawn --transpile-only src/index.ts" 6) add IntelliJ IDEA npm for "server:watch" script 7) npm install dotenv 8) in index.ts add: dotenv.config(); 9) create .env file in root dir of your project and add text below in .env file: PORT = 5000 const port = process.env.PORT;
- Adding log4js and eslint to Node.JS application youtu.be/qcSpd6N7ZJ8
1) npm install log4js 2) in index.ts file: import log4js from 'log4js'; ... const logger = log4js.getLogger(); logger.level = process.env.LOG_LEVEL; ... 4) in .env file: LOG_LEVEL=error 5) in index.ts file: ... logger.info('log4js log info'); logger.debug('log4js log debug'); logger.error('log4js log error'); ... 6) npm install eslint --save-dev 7) eslint --init 8) "prebuild": "npm run lint" 9) "lint:fix": "eslint --cache --ext .ts . --fix", 10) "lint": "eslint --cache --ext .ts .", !!! --cache (only changed), . 11) IntelliJ IDEA -- file -- setting -- eslint -- automatic 12) "rules": { "semi": ["error", "always"] }
- Routing controllers for Node.js youtu.be/_7z5Zubsdps
Use routing-controllers for more convenient work.
1) npm install routing-controllers 2) npm install reflect-metadata 3) npm install express body-parser multer 4) npm install class-transformer class-validator 5) tsconfig.json "compilerOptions": { ... "emitDecoratorMetadata": true, "experimentalDecorators": true ... } 6) in index.ts // const app = express(); // logger.info('log4js log info'); // logger.debug('log4js log debug'); // logger.error('log4js log error'); // app.get('/', (request, response) => { // response.send('Hello world2!'); // }); 7) in index.ts import { createExpressServer } from 'routing-controllers'; import { UserController } from './UserController'; const app = createExpressServer({ controllers: [UserController], // we specify controllers we want to use }); 8) controller/user-controller.ts import { Controller, Get, Param } from 'routing-controllers'; import 'reflect-metadata'; @Controller() export class UserController { @Get('/users/:id') getOne (@Param('id') id: number) { return 'This action returns user #' + id; } } 9) http://localhost:3001/users/1
- Node.JS middleware, interceptor, http context youtu.be/iWUMUa7gTTQ
1) middleware -- middleware.ts 2) middleware.ts export function loggingBefore (request: any, response: any, next?: (err?: any) => any): any { console.log('do something Before...'); next(); } export function loggingAfter (request: any, response: any, next?: (err?: any) => any): any { console.log('do something After...'); next(); } 3) user-controller.ts in class @UseBefore(loggingBefore) @UseAfter(loggingAfter) console.log('do something in GET function...'); 4) user-controller.ts in function @UseBefore(loggingBefore) @UseAfter(loggingAfter) 5) user-controller.ts in function @UseInterceptor(function (action: Action, content: any) { console.log('change response...'); return content; }) 6) npm install express-http-context 7) index.ts const app: Express = express(); app.use(bodyParser.json()); app.use(httpContext.middleware); useExpressServer(app, { controllers: [UserController] }); app.use((req, res, next) => { httpContext.ns.bindEmitter(req); httpContext.ns.bindEmitter(res); }); 8) middleware.ts loggingBefore import httpContext from 'express-http-context'; console.log('set traceId = 123'); httpContext.set('traceId', 123); 9) middleware.ts loggingAfter console.log(`tracedId = ${httpContext.get('traceId')}`);
- Adding POST request to Node.JS web application, validation of input data, global error handler youtu.be/onBVkkLEuw4
1) in user-controller.ts add: ... @Post('/users/:id') @OnUndefined(204) postOne (@Param('id') id: number, @Body() info: any) { console.log(JSON.stringify(info)); } ... 2) in postman http://localhost:3001/users/1 { "country":"Russia", "city":"SPb" } 3) model -- info.ts 4) import { IsDefined } from 'class-validator'; export class Info { @IsDefined() country: string; @IsDefined() city: string; } 8) postOne (@Param('id') id: number, @Body() info: Info) { 9) middleware -- global-error-handler.ts 10) import { ExpressErrorMiddlewareInterface, Middleware } from 'routing-controllers'; @Middleware({ type: 'after' }) export class GlobalErrorHandler implements ExpressErrorMiddlewareInterface { error (error: any, request: any, response: any, next: () => any) { response.send({ ERROR: error }); next(); } } 11) useExpressServer(app, { controllers: [UserController], // we specify controllers we want to use middlewares: [GlobalErrorHandler], defaultErrorHandler: false });
- Swagger documentation in Node.JS web application youtu.be/-uoIasCbsq8
1) npm install swagger-ui-express 2) tsconfig.json -- "resolveJsonModule": true 3) src -- swagger -- openapi.json 4) index.ts import swaggerUi from 'swagger-ui-express'; import * as swaggerDocument from '../src/swagger/openapi.json'; ... app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); 5) change port to 3000 in .env file set PORT=3000 6) npm install cors 7) npm install @types/cors 8) in index.ts import cors from 'cors'; ... app.use(cors() as RequestHandler); ... 9) Swagger Editor (example for test project) openapi openapi: 3.0.1 info: title: test API version: v1 servers: - url: 'http://localhost:3000' tags: - name: API functions description: >- API functions of our application paths: /users/{id}: get: summary: returns simple answer from get tags: - API functions parameters: - name: id in: path required: true description: simple parameter schema: type : string example: '1' description: parameter id just for test responses: '200': #status code description: OK content: document: schema: type: string example: some text post: summary: returns simple answer from post tags: - API functions requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/Info' example: country: Russia city: Spb parameters: - name: id in: path required: true description: simple parameter schema: type : string example: '1' description: parameter id just for test responses: '204': #status code description: OK components: schemas: Info: type: object properties: country: type: string city: type: string
- Adding Unit tests on Jest in Node.JS web application youtu.be/rCIRpTMVEMM
0) in global-error-handler.ts response.status(error.statusCode || error.httpCode).json(error); next(); 1) npm install --save-dev jest 2) npm i -D ts-jest @types/jest 3) npm i -D ts-jest 4) package.json -- { ... scripts { ... "test:unit": "jest --config=jest.config.js", }, ... } 5) create jest.config.js with code below: process.env.NODE_ENV = 'UNITTEST'; module.exports = { clearMocks: true, collectCoverage: true, collectCoverageFrom: [ './src/**/*.ts' ], coverageDirectory: '<'rootDir>/test/coverage', testEnvironment: 'node', testMatch: ['**/*.test.ts'], preset: 'ts-jest' }; 6) .eslintignore *.js node_modules dist coverage } 7) .eslintrc.json { ... "env": { "jest": true } ... } 8) test -- controller -- user-controller.test.ts describe('UserController', () => { afterEach(() => { jest.restoreAllMocks(); }); it('postOne', () => { const userController = new UserController(); const testBody = { city: 'SPb' }; const res = userController.postOne(1, testBody as Info); expect(res).toBeUndefined(); }); } 9) in IDEA add script - test:unit set in environment - NODE_ENV=UNITTEST 10) Simple variant of jest.config.js for IDEA: process.env.NODE_ENV = 'UNITTEST'; module.exports = { clearMocks: true, collectCoverage: false, testEnvironment: 'node', testMatch: ['**/*.test.ts'], preset: 'ts-jest' }; 11) npm i -D supertest @types/supertest 12) in user-controller.test.ts ... let server; ... beforeAll(async () => { server = express(); server.use(bodyParser.json()); useExpressServer(server, { controllers: [UserController], // we specify controllers we want to use middlewares: [GlobalErrorHandler], defaultErrorHandler: false }); }); ... it('postOne with validations', done => { request(server) .post('/users/1') .send({ country: 'Russia', city: 'SPb' } as Info) .expect(204) .end((err, res) => { if (err) throw new Error(JSON.stringify(res.body)); done(); }); });
- Using Config package for Node.JS and other useful packages youtu.be/8ZCHUN-JTck
Config package allows to set value for constants depending on NODE_ENV value.
1) npm install config 2) npm install @types/config 3) config 4) default.yaml PORT: 3000 DEV.yaml PORT: 3001 LOCAL.yaml PORT: 3002 5) index.ts // const port = process.env.PORT; const port = config.get('PORT'); 6) IDEA server:watch -- Environment NODE_ENV=DEV NODE_ENV=LOCAL -- packages: husky - commits in Git semantic-release - Commits formatting and version control pretty-quick - run prettier for changed files prettier - code formtatter eslint-config-prettier - resolve conflicts between eslint and prettier eslint-plugin-prettier - run prettier as eslint rules mock-socket - websocket mock jest-websocket-mock - websocket testing jest-sonar-reporter - convert reports from jest format to sonar format jest-mock-extended - mocks for objects and interfaces ws - websocket typescript-string-operations - String.format lodash - useful functions for js http-status-codes - constants for HTTP statuses moment - time library for js ncp - copy files js-yaml - working with yaml files mongodb - package with functions for Mongo migrate-mongo - migration for Mongo log-timestamp - write current time in log messages axios - HTTP client applicationinsights - integration with Azure Application Insights