{"id":316172,"date":"2021-01-11T09:01:03","date_gmt":"2021-01-11T09:01:03","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=316172"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=316172","title":{"rendered":"\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Node.JS, Express \u0438 Typescript \u0441 Jest, Swagger, log4js \u0438 Routing-controllers"},"content":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u042d\u0442\u043e \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u0430\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Node.JS, \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c typescript \u0438 express. \u041d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0435 \u0447\u0430\u0441\u0442\u043e, \u043e\u0442\u0441\u044e\u0434\u0430 \u0437\u0430\u0431\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0448\u0430\u0433\u0438 \u043f\u043e \u0435\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e. \u0418 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u043a\u0443\u044e \u0448\u043f\u0430\u0440\u0433\u0430\u043b\u043a\u0443, \u0432 \u043f\u043e\u043c\u043e\u0449\u044c \u0441\u0430\u043c\u043e\u043c\u0443 \u0441\u0435\u0431\u0435 \u0438 \u0434\u0440\u0443\u0433\u0438\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c. \u041f\u043e\u043c\u0438\u043c\u043e \u0448\u0430\u0433\u043e\u0432, \u044f \u0442\u0430\u043a \u0436\u0435 \u0441\u043d\u044f\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u0438\u0434\u0435\u043e \u0440\u043e\u043b\u0438\u043a\u0438 \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438. \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0434\u043b\u044f Node.JS, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0436\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438, \u043d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0443\u0442\u044c. \u0418\u0434\u0435\u044f \u0431\u044b\u043b\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442\u044c \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u043e\u0442 \u043a\u0430\u043a\u043e\u0433\u043e-\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u043d\u044f\u0442\u044c \u043e\u0434\u043d\u0438 \u043f\u0430\u043a\u0435\u0442\u044b \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435.<br \/>  <a name=\"habracut\"><\/a><br \/>  \u0418\u0442\u0430\u043a \u043f\u043e \u0448\u0430\u0433\u0430\u043c:<\/p>\n<ol>\n<li> <b>\u041f\u0440\u043e\u0441\u0442\u043e\u0435 Web \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 <\/b> <a href=\"https:\/\/youtu.be\/7MIIcFDeSg4\" rel=\"nofollow\">youtu.be\/7MIIcFDeSg4<\/a>\n<p>  \u0421\u0442\u0430\u0432\u0438\u043c \u0432 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u043e\u043c \u043f\u043e\u0440\u044f\u0434\u043a\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0438 Node.JS, \u0430 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438. <\/p>\n<pre><code class=\"plaintext\">1) node.js download,  2) Create directory for your project,  3) npm init,  4) in package.json  &quot;main&quot;: &quot;dist\/index.js&quot;,   &quot;scripts&quot;: {     &quot;build&quot;: &quot;tsc&quot;,     &quot;start&quot;: &quot;node .&quot;   } 5) npm install --save-dev typescript,  6) in tsconfig.json    {   &quot;compilerOptions&quot;: {     &quot;esModuleInterop&quot;: true,     &quot;outDir&quot;: &quot;dist&quot;,     &quot;baseUrl&quot;: &quot;.&quot;   } } 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) =&gt; {   response.send('Hello world!'); }); app.listen(port, () =&gt; console.log(`Running on port ${port}`)); 13) npm run build,  14) npm run start,  15) localhost:5000 <\/code><\/pre>\n<p>  <\/li>\n<li><b>\u041e\u0442\u043b\u0430\u0434\u043a\u0430 \u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432 Node.js <\/b> <a href=\"https:\/\/youtu.be\/hfST0e1ITGw\" rel=\"nofollow\">youtu.be\/hfST0e1ITGw<\/a>\n<p>  \u041d\u0430\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u0435\u043c \u0440\u0435\u0436\u0438\u043c \u043e\u0442\u043b\u0430\u0434\u043a\u0438 \u0438 \u0441\u043e\u0437\u0434\u0430\u0435\u043c .env \u0444\u0430\u0439\u043b \u0434\u043b\u044f \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0438 \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439.<\/p>\n<pre><code class=\"plaintext\">1) in tsconfig.json add: &quot;sourceMap&quot;: true 2) int package.json add: &quot;prestart&quot;: &quot;npm run build&quot;, 3) In IntelliJ IDEA in Run\/Debug Configurations choose: &quot;npm&quot; and add script 4) npm i ts-node-dev --save-dev 5) int package.json add: &quot;server:watch&quot;: &quot;ts-node-dev --respawn --transpile-only src\/index.ts&quot; 6) add IntelliJ IDEA npm for &quot;server:watch&quot; 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; <\/code><\/pre>\n<p>  <\/li>\n<li><b>\u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u0435 log4js \u0438 eslint \u043a \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043d\u0430 Node.JS <\/b> <a href=\"https:\/\/youtu.be\/qcSpd6N7ZJ8\" rel=\"nofollow\">youtu.be\/qcSpd6N7ZJ8<\/a>\n<pre><code class=\"plaintext\">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) &quot;prebuild&quot;: &quot;npm run lint&quot; 9) &quot;lint:fix&quot;: &quot;eslint --cache --ext .ts . --fix&quot;, 10) &quot;lint&quot;: &quot;eslint --cache --ext .ts .&quot;,     !!! --cache (only changed), . 11) IntelliJ IDEA -- file -- setting -- eslint -- automatic 12) &quot;rules&quot;: {         &quot;semi&quot;: [&quot;error&quot;, &quot;always&quot;]     } <\/code><\/pre>\n<p>  <\/li>\n<li><b>Routing controllers \u0434\u043b\u044f Node.js <\/b> <a href=\"https:\/\/youtu.be\/_7z5Zubsdps\" rel=\"nofollow\">youtu.be\/_7z5Zubsdps<\/a>\n<p>  \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c routing-controllers \u0434\u043b\u044f \u0431\u043e\u043b\u0435\u0435 \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<pre><code class=\"plaintext\">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    &quot;compilerOptions&quot;: {       ...       &quot;emitDecoratorMetadata&quot;: true,       &quot;experimentalDecorators&quot;: 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) =&gt; { \/\/   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 <\/code><\/pre>\n<p>  <\/li>\n<li><b>Node.JS middleware, interceptor, http context <\/b> <a href=\"https:\/\/youtu.be\/iWUMUa7gTTQ\" rel=\"nofollow\">youtu.be\/iWUMUa7gTTQ<\/a>\n<pre><code class=\"plaintext\">1) middleware -- middleware.ts 2) middleware.ts export function loggingBefore (request: any, response: any, next?: (err?: any) =&gt; any): any {   console.log('do something Before...');   next(); }  export function loggingAfter (request: any, response: any, next?: (err?: any) =&gt; 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) =&gt; {    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')}`); <\/code><\/pre>\n<p>  <\/li>\n<li><b>Node.JS \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c post \u0437\u0430\u043f\u0440\u043e\u0441, \u0432\u0430\u043b\u0438\u0434\u0430\u0446\u0438\u044f \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445, \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a <\/b> <a href=\"https:\/\/youtu.be\/onBVkkLEuw4\" rel=\"nofollow\">youtu.be\/onBVkkLEuw4<\/a>\n<pre><code class=\"plaintext\">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  {  &quot;country&quot;:&quot;Russia&quot;,  &quot;city&quot;:&quot;SPb&quot;  } 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: () =&gt; any) {     response.send({ ERROR: error });     next();   } } 11)  useExpressServer(app, {   controllers: [UserController], \/\/ we specify controllers we want to use   middlewares: [GlobalErrorHandler],   defaultErrorHandler: false }); <\/code><\/pre>\n<p>  <\/li>\n<li><b>Swagger \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f \u0432 Node.JS \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 <\/b> <a href=\"https:\/\/youtu.be\/-uoIasCbsq8\" rel=\"nofollow\">youtu.be\/-uoIasCbsq8<\/a>\n<pre><code class=\"plaintext\">1) npm install swagger-ui-express 2) tsconfig.json -- &quot;resolveJsonModule&quot;: 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: &gt;-       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 <\/code><\/pre>\n<p>  <\/li>\n<li><b>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c Unit \u0442\u0435\u0441\u0442\u044b \u043d\u0430 Jest \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Node.JS <\/b> <a href=\"https:\/\/youtu.be\/rCIRpTMVEMM\" rel=\"nofollow\">youtu.be\/rCIRpTMVEMM<\/a>\n<pre><code class=\"plaintext\">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 { ... &quot;test:unit&quot;: &quot;jest --config=jest.config.js&quot;, }, ... } 5) create jest.config.js with code below:  process.env.NODE_ENV = 'UNITTEST'; module.exports = {     clearMocks: true,     collectCoverage: true,     collectCoverageFrom: [         '.\/src\/**\/*.ts'     ],     coverageDirectory: '&lt;'rootDir&gt;\/test\/coverage',     testEnvironment: 'node',     testMatch: ['**\/*.test.ts'],     preset: 'ts-jest' };  6) .eslintignore *.js node_modules dist coverage } 7) .eslintrc.json { ... &quot;env&quot;: {    &quot;jest&quot;: true }  ... }  8) test -- controller -- user-controller.test.ts  describe('UserController', () =&gt; {   afterEach(() =&gt; {     jest.restoreAllMocks();   });    it('postOne', () =&gt; {     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 () =&gt; {     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 =&gt; {     request(server)       .post('\/users\/1')       .send({         country: 'Russia',         city: 'SPb'       } as Info)       .expect(204)       .end((err, res) =&gt; {         if (err) throw new Error(JSON.stringify(res.body));         done();       });   }); <\/code><\/pre>\n<p>  <\/li>\n<li><b>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 config \u0434\u043b\u044f Node.JS, \u0430 \u0442\u0430\u043a \u0436\u0435 \u0434\u0440\u0443\u0433\u0438\u0435 \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b. <\/b> <br \/>  <a href=\"https:\/\/youtu.be\/8ZCHUN-JTck\" rel=\"nofollow\">youtu.be\/8ZCHUN-JTck<\/a>\n<p>  \u041f\u0430\u043a\u0435\u0442 config \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442 \u043f\u0440\u0438 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f NODE_ENV.<\/p>\n<pre><code class=\"plaintext\">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 - \u043a\u043e\u043c\u043c\u0438\u0442\u044b \u0432 \u0433\u0438\u0442 semantic-release - \u0444\u043e\u0440\u043c\u0430\u0442 \u043a\u043e\u043c\u043c\u0438\u0442\u043e\u0432 \u0438 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u044c \u0432\u0435\u0440\u0441\u0438\u0439  pretty-quick - \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 prettier \u043d\u0430 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u043d\u044b\u0445 \u0444\u0430\u0439\u043b\u0430\u0445 prettier - \u0444\u043e\u0440\u043c\u0430\u0442 \u043a\u043e\u0434\u0430 eslint-config-prettier - \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u044b \u043c\u0435\u0436\u0434\u0443 eslint \u0438 prettier eslint-plugin-prettier - \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u0442 prettier \u043a\u0430\u043a \u043f\u0440\u0430\u0432\u0438\u043b\u0430 eslint  mock-socket - \u043c\u043e\u043a \u0434\u043b\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430 jest-websocket-mock - \u0442\u0435\u0441\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430 jest-sonar-reporter - \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0440 \u0438\u0437 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 jest \u0432 \u0444\u043e\u0440\u043c\u0430\u0442 sonar jest-mock-extended - \u043c\u043e\u043a \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432  ws - \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442  typescript-string-operations - String.format lodash - \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0445 \u0444\u0443\u043d\u043a\u0446\u0438\u0439 \u0434\u043b\u044f js http-status-codes - \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b \u0434\u043b\u044f HTTP \u0441\u0442\u0430\u0442\u0443\u0441\u043e\u0432 moment - \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u0440\u0430\u0431\u043e\u0442\u044b \u0441\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0435\u043c \u0432 js  ncp - \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 js-yaml - \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 yaml \u0444\u0430\u0439\u043b\u043e\u0432  mongodb - \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 Mongo migrate-mongo - \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0434\u043b\u044f Mongo  log-timestamp - \u0437\u0430\u043f\u0438\u0441\u044c \u0434\u0430\u0442\u044b \u0432 \u043b\u043e\u0433  axios - HTTP \u043a\u043b\u0438\u0435\u043d\u0442  applicationinsights - \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u044f \u0441 Azure Application Insights <\/code><\/pre>\n<p>  <\/li>\n<\/ol>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/536512\/\"> https:\/\/habr.com\/ru\/post\/536512\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text-html post__text_v1\" id=\"post-content-body\">\u042d\u0442\u043e \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u0430\u044f \u0438\u043d\u0441\u0442\u0440\u0443\u043a\u0446\u0438\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430 Node.JS, \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c typescript \u0438 express. \u041d\u043e\u0432\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0435 \u0447\u0430\u0441\u0442\u043e, \u043e\u0442\u0441\u044e\u0434\u0430 \u0437\u0430\u0431\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0448\u0430\u0433\u0438 \u043f\u043e \u0435\u0433\u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e. \u0418 \u044f \u0440\u0435\u0448\u0438\u043b \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0435\u043a\u0443\u044e \u0448\u043f\u0430\u0440\u0433\u0430\u043b\u043a\u0443, \u0432 \u043f\u043e\u043c\u043e\u0449\u044c \u0441\u0430\u043c\u043e\u043c\u0443 \u0441\u0435\u0431\u0435 \u0438 \u0434\u0440\u0443\u0433\u0438\u043c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c. \u041f\u043e\u043c\u0438\u043c\u043e \u0448\u0430\u0433\u043e\u0432, \u044f \u0442\u0430\u043a \u0436\u0435 \u0441\u043d\u044f\u043b \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0438\u0435 \u0432\u0438\u0434\u0435\u043e \u0440\u043e\u043b\u0438\u043a\u0438 \u0434\u043b\u044f \u043d\u0430\u0433\u043b\u044f\u0434\u043d\u043e\u0441\u0442\u0438. \u0421\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0443\u0436\u0435 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0438 \u0434\u043b\u044f Node.JS, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0443\u0436\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442 \u0432 \u0441\u0435\u0431\u0435 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 \u043f\u0430\u043a\u0435\u0442\u044b \u0438 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0441 \u043d\u0438\u043c\u0438, \u043d\u043e \u044d\u0442\u043e \u0443\u0436\u0435 \u0434\u0440\u0443\u0433\u043e\u0439 \u043f\u0443\u0442\u044c. \u0418\u0434\u0435\u044f \u0431\u044b\u043b\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u0438\u0442\u044c \u0446\u0435\u043b\u0438\u043a\u043e\u043c \u043e\u0442 \u043a\u0430\u043a\u043e\u0433\u043e-\u0442\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430 \u0438 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u043c\u0435\u043d\u044f\u0442\u044c \u043e\u0434\u043d\u0438 \u043f\u0430\u043a\u0435\u0442\u044b \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0435.  <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-316172","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/316172","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=316172"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/316172\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=316172"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=316172"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=316172"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}