This article is my notes on learning Node +typescript, some of which are portable, and the next one is the code for the login interface.

Project preparation

I. Selection of technology stack

Develop the class

Typescript

TypeScript is an open source programming language that improves your development experience by adding static type definition generation to JavaScript, one of the world’s most commonly used languages, to find errors and provide fixes before you run your code. It is a superset of JavaScript, and essentially adds optional static typing and class-based object-oriented programming to the language.

The framework

Koa is a simple and easy-to-use Node.js Web framework that provides a foundation for developing smaller, more expressive, and more reliable Web applications and apis.

The database

The MySQL database

sequelize-typescript

A richer and more powerful database operation library.

Day.js

Day.js is a lightweight JavaScript library that handles times and dates, keeping exactly the same API design as moment. js

Jsonwebtoken

JSON Web Token is a cross-domain authentication solution, or JWT for short.

Second, standard interface

Refer to the article: developer.aliyun.com/article/283…

In the development of the program, a good interface specification can make the front and back end of the cooperation more smooth, front-end and back-end interface development, 1, can write a unified network request function and data return processing function, 2, can be unified to the error processing and analysis.

Some basic fields on the back end

Address of the interface

The general rule is: basePath/ API /v1/login

The request mode of the interface

The common interface request modes are GET and POST

The interface parameters

It is usually an object, for example:

{
    userAccount:' '.userPassword:' '
}
Copy the code
Interface Version number

The front end can pass Version =1.0 to request a responsive version of the interface.

Status code returned by the interface

Normally this field is represented by code

We use the status code of HTTP for reference, and the interface specifications of the front and back ends to identify whether an interface is successfully invoked. It is better to also use the status code to identify whether an interface is successfully invoked. For example, 200 indicates success, 4 indicates that the interface does not exist, and 5 indicates that the interface is incorrectly invoked

Master data returned by the interface

This field is typically represented by data

Interface Indicates the prompt message returned to the user

When an error occurs on the server, a message is displayed to the user

Data Field Description

Array field List

When an array is returned, there are many additional fields to identify it, so the array data needs to be placed in a list.

Page: the page number

This is required when there are pages, in which the start page is 1 instead of 0

PageSize Number of pages to display per page

Need to return when there is pagination

TotallPage total number of pages

The total number of pages of data that need to be returned when data is paginated

TotalCount total number

Sometimes the front end needs to return a total of data, so you need to return the total

data:{
    page:1,
    pageSize: 1,
	list: [{},{}, {}]
}
Copy the code

3. Be familiar with HTTP response status

HTTP response diagram:

Typescript configuration

Configuration file tsconfig.json, the configuration code is as follows:

{
    "compilerOptions": {
        "allowJs": true./ / allow JS
        "outDir": "./dist".// Output file
        "module": "commonjs".// Module type
        "target": "es2016".// Compile target
        "sourceMap": true.// Displays the original TypeScript source code
        "noImplicitAny": true.// Implicit type checking
        "strictNullChecks": true.// Strict null checking
        "experimentalDecorators": true./ / metadata
        "emitDecoratorMetadata": true./ / a decorator
        "resolveJsonModule": true.// Allow to import. Json modules
        "esModuleInterop": true.// Es modules interoperate
        "allowSyntheticDefaultImports": true// Allow default import
    },
    "include": [
        "./src/**/*"]}Copy the code

ts-node-dev

Hot overloading is implemented (changing the code automatically restarts the service).

Installation:

npm i ts-node-dev --save-dev
yarn add ts-node-dev --dev
Copy the code

Fifth, koa – ts – controllers

In KOA-TS-Controllers, we use TypeScript decorators to route bindings directly within a method of a given class. And through the function it provides, we can more convenient to achieve more RESTful specification interface, let’s see the specific use:

Installation and Configuration

Installation of koa – ts – controllers

npm i koa-ts-controllers
Copy the code

Add code to entry file

import {bootstrapControllers} from 'koa-ts-controllers';
import path from 'path';
import KoaRouter from 'koa-router';

const app = new Koa();
const router = new KoaRouter();

(async() = > {await bootstrapControllers(app, {
        router,
        basePath: '/api'.versions: [1].controllers: [
            __dirname + '/controllers/**/*']}); app.use( router.routes() ); app.listen(configs.server.port, configs.server.host,() = > {
      	console.log('Access started successfully: http://${configs.server.host}:${configs.server.port}`); }); }) ()Copy the code

BootstrapControllers is a koA-TS-controllers function that initializes the binding of application controllers and routes.

configuration

router

Routing library used

basePath

Set the prefix of the access interface path, such as basePath/ API

versions

The interface version is appended to basePath, such as basePath/ API /v1

controllers

Directory for storing controller class files.

The controller

We can put the control class file in the controllers directory, of course, just such a class is not useful, we also need to turn the class into a control class and bind the class methods to the specified route, this needs to use the decorator.

A decorator

The Controller decorator

Grammar: @ Controller (basePath?

For class decorators, the decorated class becomes a controller class, and only methods under the controller class can be bound to routes.

BasePath: prefix path of routes bound to methods in this class.

HTTP request method decorator

Koa-ts-controllers support the following method decorators:

  • @Get(path)
  • @Post(path)
  • @Patch(path)
  • @Put(path)
  • @Delete(path)

These decorators, used in class methods, take a path parameter and bind the decorated class method to the specified HTTP method and its merged path.

The code examples
import {Controller, Get} from 'koa-ts-controllers';
@Controller('/test')
class TestController {
  	@Get('/hello')
  	async sayWorld() {
      	return 'Hello Test! '; }}Copy the code

To start the service, we can use:

http://localhost:8080/api/v1/test/hello this path to access the API.

Back-end – Request data acquisition

Get request data

Usually, the client sends some extra data based on service requirements. Data can be transmitted in the following scenarios:

Params

Dynamic routing variable part: http://localhost:8080/api/v1/test/hello/1

Query

Address a question mark after part: http://localhost:8080/api/v1/test/hello? id=1

Body

Request body:

{
	account:'shaobing',
 	password:'123456'}Copy the code
headers

In addition to some built-in headers, you can also customize some of the header information. The user authorization token in our application is passed through the header message.

Koa-ts-controllers provide not only request method decorators to process requests, but also parameter decorators to process request data.

  • @params () with the following code:

    import {Controller, Get, Params} from "koa-ts-controllers";
    
    @Controller('/test')
    class UserController {
      	@Get('/user/:id')
      	async getUser(@Params() params: {id: number}) {
    		return 'The current ID is:${params.id}`
    	}
      	@Get('/user/:id')
      	async getUser(@Params('id') id: number) {
    		return 'The current ID is:${id}`}}Copy the code
  • @query (), with the following code:

    import {Controller, Get, Query} from "koa-ts-controllers";
    @Controller('/test')
    class UserController {
      	@Get('/user')
      	async getUser(@Query() params: {id: number}) {
    		return 'The current ID is:${params.id}`
    	}
      	@Get('/user')
      	async getUser(@Query('id') id: number) {
    		return 'The current ID is:${id}`}}Copy the code
  • @body (), which looks like this:

    import {Controller, Get, Body} from "koa-ts-controllers";
    @Controller('/test')
    class LoginController {
      	@Post('/login')
      	async index(@Body() body: {
            account: string,
            password:string
        }) {
    		return 'The current account password is yes:The ${JSON.stringify(body)}`}}Copy the code
  • @header (), with the following code:

    import {Controller, Get, Header} from "koa-ts-controllers";
    @Controller('/test')
    class LoginController {
      	@Post('/login')
      	async getUser(@Header() header:any) {
    		console.log(header); }}Copy the code

Back-end – Unified response processing

Successful unified response processing

In general, the processing of a successful response is simple and the status code returned is 200

Error unified response handling

In general, the handling of an error response can be divided into server errors, business logic errors, request interface nonexistence, and so on.

The common status code of a server error is 500. The common status code of a request interface does not exist is 404. The common status code of a service logic error is 422 or 401.

Error capture processing

We can use the koA-TS-Controllers unified error handler to catch errors and output them simultaneously.

await bootstrapControllers(app, {
        router,
        basePath: '/api'.versions: [1].controllers: [
            __dirname + '/controllers/**/*'].errorHandler: async (err: any.ctx: Context) => {
            let status = 500;
            let body: any = {
                "statusCode": 500."error": "Internal Server error"."message": "An internal server error occurred"}; ctx.status = status; ctx.body = body; }});Copy the code

Validate request data

In the case of params, we can verify this directly with the re method, for example:

@Get('/user/:id(\\d+)')
async index(@Params() params:{id:number}){
    return params.id;
}
Copy the code

In the case of Query and Body, we can use the class-Validator library for uniform validation, the class-Validator address.

For example, if we want to verify that the string passed in is a number, we can use IsNumberString as follows:

import {Controller, Get,Query} from "koa-ts-controllers";
import {IsNumberString} from 'class-validator';
class GetUsersQuery {
    @IsNumberString(a)page: number;
}
@Get('/users')
public async getUsers(@Query() query: GetUsersQuery){
     console.log(query);
}
Copy the code

Validation returns data returned by failure

{
    "data": [{"field": "page"."violations": {
                "isNumberString": "page must be a number string"}}]."isBoom": true."isServer": false."output": {
        "statusCode": 422."payload": {
            "statusCode": 422."error": "Unprocessable Entity"."message": "validation error for argument type: query"
        },
        "headers": {}}}Copy the code

Using the above data we can take the error-catching function one step further,

await bootstrapControllers(app, {
        router: router,
        basePath: '/api'.versions: [1].controllers: [
            __dirname + '/controllers/**/*'].errorHandler: async (err: any.ctx: Context) => {
            let status = 500;
            let body: any = {
                "statusCode": 500."error": "Internal Server error"."message": "An internal server error occurred"
            };
            if(err.output) { status = err.output.statusCode; body = {... err.output.payload};if(err.data) { body.errorDetails = err.data; } } ctx.status = status; ctx.body = body; }});Copy the code