I’m going to give the framework a name, not to think of any weird names, but following the Spring Boot design, I’m going to give the framework a name Summer

Making:Github.com/calidan-x/s…

The preparation refers to the mature midwayJS and Nest on the market

____ _ _ __ __ __ __ _____ _____ / ___ | | | | | \ there comes / | \ | | _ \ \ _ \ | | | | | \ / | | | \ / | | _ | | | | ___ _)) | | _ | | | | | | | | |___| _ < |____/ \___/|_| |_|_| |_|_____|_| \_\ Ver0.01.

[2021-03-16 13:12:46] MySQL DB connected
[2021-03-16 13:12:46] Server started port:8080
Copy the code

Framework Design Idea

The loc. Ts reverse control code can be said to be very simple. You just need to encapsulate the methods for storing and reading instances.

Route-mapping.ts is one of the most difficult parts of routing. You need to read method arguments in the Controller, request methods corresponding to the method, and route prefixes for the controller. This decorator data is eventually assembled into a routeMapping global.

Decorators /*.ts All decorators (annotations) here fall into 3 categories:

  1. Restful Request route related @controller @get @Post @PUT @delete@patch
  2. RequestParam @pathparam @requestBody
  3. Loc container related @service @Component @autowired
  • For decorators used in Controller, such as @controller@get@pathparam, call methods in route-Mapping to synthesize routeMapping data.
  • For Controller, Service, etc., the default Loc container decorator (@controller@servicer@Component) is added to the Loc container by calling the instance add method in loc.ts.

The final generated structure is roughly as follows:

[
  '/persons': {
    GET: { callMethod: 'personList'.params: []},POST: { callMethod: 'addPerson'.params: [Array]},controllerName: 'PersonController'.pathRegExp: /^\/persons[\/#\?] ? $/i,
    pathKeys: [],
    controller: PersonController { personService: [PersonService] }
  },
  '/persons/:id': {
    GET: { callMethod: 'personInfo'.params: [Array]},controllerName: 'PersonController'.pathRegExp: /^\/persons(? : \ / ([^ # \ \ /?] +? ) / # \ \ /? ? $/i,
    pathKeys: [ [Object]],controller: PersonController { personService: [PersonService] }
  }
]
Copy the code

Server. ts Server main class, encapsulating methods that listen on ports.

  • When a route request is received, a fully matched route can be found first to speed up the matching speed, but the route from the re cannot be found again.
  • QueryParam pathParam requestBody = queryParam pathParam requestBody = queryParam pathParam Write method return data to request return data.

Database. ts database connection classes that integrate TypeORM after trying Sequelize-typescript and TypeORM

The resouces directory holds the project configuration

Logger. ts Log printing class

Summer. ts framework main entrance, scan injection Loc singleton, connect to database, start port listening

Problems encountered during development

  1. Can @pathparam () be shortened to PathParam when the parameter is null? Because of TypeScript language limitations, this can’t be done without overloading methods, but given that most of the time the names of participating variables are the same, it was eventually designed. PathParam+PathParamFrom().
  2. How does ParaParam read method variable names? The method toString() is read from the argument.
  3. How to validate data from calling interfaces? Use the class – the validator.
  4. Various DTOS and Entity conversion, in the Java environment often need to do object and Json format conversion repeatedly, is there a convenient method in the NodeJS environment? After research, you can set useDefineForClassFields in tsconfig.json to true and target to at least ES6 so that you can export class empty fields and then instantiate and iterate over the imported data.

Practice framework, try to build the project to get some code practice recommendations

Create a simple project to create a Person table in the database, implement the Person table read add a series of interfaces

The Controller class

import { Controller, Get, Autowired, PathParam, Post, RequestBody } from '.. /.. /lib/decorators';
import { PersonRequest } from '.. /dto/request/person-request';
import { PersonResource } from '.. /dto/resource/person-resource';
import { PersonService } from '.. /service/person-service';

@Controller('/persons')
export class PersonController {
  @Autowired
  personService: PersonService;

  @Get(' ')
  async personList() {
    const persons = await this.personService.getPersons();
    return persons;
  }

  @Post(' ')
  async addPerson(@RequestBody personRequest: PersonRequest) {
    const persons = await this.personService.savePerson(personRequest.toPerson());
    return persons;
  }

  @Get('/:id')
  async personInfo(@PathParam id: string) {
    const person = await this.personService.getPersonInfo(id);
    returnPersonResource.from(person); }}Copy the code

The Service class

import { getRepository } from 'typeorm';
import { Service } from '.. /.. /lib/decorators';
import { Person } from '.. /entity/person';

@Service
export class PersonService {
  personRepository = getRepository(Person);

  async getPersons() {
    const person = await this.personRepository.find();
    return person;
  }

  async getPersonInfo(id: string) {
    const person = await this.personRepository.findOne(id);
    return person;
  }

  async savePerson(person: Person) {
    await this.personRepository.save(person); }}Copy the code

Code practice suggestions:
  1. Use class instead of interface/type. Since both Entity and validation classes require class, backend typescript development uses class instead of interface/type, which is common on the front end.
  2. TypeScript can check types internally, but guard against external input data because TypeScript can’t do that at runtime.
  3. TypeORM works better than Sequelize-typescript.

NodeJS compares the advantages and disadvantages with Java backend frameworks

Advantages: small memory footprint, fast startup, no need to deal with multi-threading, non-blocking high concurrency, less Setter getters and Java various List/ArrayList/HashMap… Code is easier to write. Disadvantages: Runtime type checking, strict type determination, and TypeScript’s lack of support for reflection and Decorator make it difficult for the framework to better encapsulate advanced functionality.

conclusion

The NodeJS backend is already well established, with NPM versions of all kinds of engineering and microservice needs. Internal data is typed correctly with TypeScript validation, and can be used in real projects as long as the validation and conversion of input data is handled well.








The distance from the complete framework, the functionality not implemented in this framework
  1. AOP
  2. Timing task
  3. The message queue
  4. File upload, with form format support
  5. Verification and Security
  6. Cross-domain request Settings
  7. Framework testing/engineering testing
  8. Declarative request
  9. Template rendering
  10. API document generation
  11. Project Introduction Document
  12. … .

This article is participating in the “Nuggets 2021 Spring Recruitment Campaign”, click to see the details of the campaign