Creating web applications with javascript

Creating Node.JS web server application with Express, Typescript, Jest, Swagger, log4js and routing-controllers

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:

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 $`)); 13) npm run build, 14) npm run start, 15) localhost:5000 

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; 
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":

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 
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 = $`); 
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 >); 
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/: 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 
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: '/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(); >); >); 

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 

Источник

Читайте также:  Css как сделать disable

Creating web applications with javascript

Also another issue is I think say in adding record scenario, if loadAll fails, and book.instances is empty — then once user closes add button html file, save will be called, and data in the localStorage will be deleted, isn’t it?

in update scenario if I change title of book the new title isn’t seen in combo box — it still shows old title

Right, in this special case of an update, the content of the selection list is only updated later when you re-enter the page, and it would be preferable to have its contents updates as well.

In your second remark you say «if loadAll fails», but I don’t understand why it should fail?

saveButton.addEventListener("click", pl.view.createBook.handleSaveButtonClickEvent);

I see so it is good practice otherwise I could put it in handler attribute too

and load it in the first place.

In the app’s code, I’m using namespaces (in the form of special JS objects) for grouping code according to the Model-View-Controller achtitecture metaphor. Thus, pl.model is the namespace object for model code, pl.view is the namespace object for view/UI code, and pl.ctrl is the namespace object for controller code.

Ithas to be loaded/executed first, because these namespace objects are used in the subsequent code.

There is no such architecture concept like «view controllers» in MVC. There is only the «model» (in the form of model classes) and the «view» (the user interface), and the «controller» is all the remaining code needed to connect the model and the view with each other.

Читайте также:  W293 blank line contains whitespace python

The view does not just consist of HTML code, but also of all the JS code required for setting up the user interface and handling its events.

No, the minimal app has some controller code, but indeed, the controller part is very thin. However, this is not an issue. In a backend app, the controller has to take care of routing, which we don’t need to do in our simple fronent app.

Yeah it seems this ain’t strictly defined

Book.convertRow2Obj = function (bookRow) < var book = new Book( bookRow); return book; >;

< isbn:"006251587X," title:"Weaving the Web", year:2000 >
< constructor: Book, isbn:"006251587X," title:"Weaving the Web", year:2000 >

we want that book instanceof Book holds

It’s true that we don’t really need the test book instanceof Book at this point in the app.

The data is stored with the browser’s buil-in localStorage mechanism, which is exposed as a JavaScript API, as explained in the full article.

So, lets say I need to reformat my drive and re-load MS Win (which I do about every 6mo-1yr). How do I save my valuable data?
Thx

You would need to add an «Export/import data» function to the app, which should not be a big thing.

Notice, however, that the Local Storage technology cannot be used for larger data sets, or for business-critical apps. In these cases, you would rather use the IndexDB API or a remote storage solution like cloud storage or a back-end DBMS. I’ll show how to do this (cloud storage with Parse.com and using the back-end DBMS MySQL with NodeJS) in follow-up tutorials.

Читайте также:  Python pip install prophet

Can I some way save data to localhost / php?

Perhaps if I explain my need it will make more sense.

I am an accomplished programmer and use PHP everyday.
I have wanted to use some type of browser program to
write an airplane performance tool. If I use PHP and SQLITE
it limits users that don’t know how to set up localhost.
I have a server but the average Joe does not.

I really like JS but every time I look at a potential
application I always hit a snag with data storage issues.
Your article re-ignited my interest in JS but once again
the data storage issue arises.

General News Suggestion Question Bug Answer Joke Praise Rant Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Источник

Оцените статью