I write Java SpringBoot project on the front end | JD Cloud Technology Team

Preface

Let's play, let's make trouble, don't make fun of me! It is not recommended that you use the Node service as a C-side service, after all, it is a mechanism. This feature determines its mission from the beginning of the language design - Java >>>[Script]. I won’t explain it here. Just look at the history of JavaScript and you will know. This also determines that it cannot Like back-end languages , it can tolerate a small amount of user visits, but once it withstands a large number of concurrent visits, it will be cool~C端单线程多任务Javascript多线程多任务

So why do we still need to write Node services? The main reason is that it is convenient and fast. For small projects, construction can be completed quickly and the development cost is small. Secondly, the following gains are achieved mainly by writing:Nest

  • Learn the decorator syntax and feel its simplicity and beauty;
  • Learn a new development framework by yourself, feel the advantages and disadvantages of different frameworks, and lay the foundation for future development and selection;
  • Feel the complexity of server-side troubleshooting and find inspiration for front-end design.

This article mainly uses NestJs + Sequelize + MySQL to complete the basic operation. It will show you the basic construction of the Node server. You can also take a look at the basic structure of the Java SpringBoot project. They are really very similar. If you don’t believe me, ask about server development. classmate.

Develop a good habit. When reading an article, click three times before reading it~ [Like, Follow, and Retweet]. You can comment after reading the comments~Continue to improve and fill in the gaps~

The first step is to start the project

When choosing the server, I have used Egg.js before, so I won’t choose it this time. Secondly, Egg also inherits the development foundation of Koa, and Express is also innovative based on Koa. The two should be similar, so I don't choose Koa and Express.

Therefore, I want to try Nest.js to see that the syntax is the same as Java. In addition, I have developed a Java + SpringBoot project before. Of course, the older SSH 2.0 has also been built from scratch, that is: Spring2.0 + Struts2+Hibernate3.2, I think it should be easy to get started, and I will write about it in retrospect.

Reference documentation:

Let me tell you my thoughts. First of all, we have just started, and there will probably be a lot of unclear pitfalls. Let’s keep it simple first, and we will continue to deepen it later. Since we want to develop the server side, if we want to do it, do more. Let’s all try it out and have fun. We plan to use as the front-end framework, as the middle processing layer. We use the traditional underlying database , which is relatively stable, reliable, and relatively familiar. We will not use a new one here. After all, the database is the cornerstone of everything.NestGraphqlMySQL

Let’s talk about our specific implementation steps:

1. [Required] There is no database, the interface request is completed, and it can run;
2. [Required] Create a basic database and access the database to complete the function:MySQL@nestjs/sequelize增删改查CRUD
3. [ Optional ] We plan to process API queries to achieve accurate data queries. This has become very popular, but it is rarely used. We plan to experience it first, and then we can directly use it in business.Graphql
4. [ Optional ] Access to automatically generate API documents and quickly conduct joint debugging and testing of front-end and back-end services.Swagger
Swagger is an open source tool for designing, building, documenting, and consuming RESTful web services.
5. [Optional] Interface request, database optimization processing
◦Request offloading, database write locking, and concurrent process processing
Added middleware to uniformly process requests and responses, perform authentication processing, request interception and other operationsmiddleware
◦Database split backup and database disaster management, divided into: primary, backup and disaster
Separate reading and writing of database, double-write data, establish database caching mechanism, and use processingredis

You are also welcome to add more optimization points, and we will discuss them together~ If you are interested, you can help add code~

After determining the general direction, we started to organize. Let’s not pursue one step, otherwise the more things we add, the more chaos we will get. We can add icing on the cake later, and we must prioritize the completion of basic functions. Nest.js official website: https://docs.nestjs.com/ Without further ado, let’s get straight to the details.

# 进入文件夹目录
cd full-stack-demo/packages
# 安装脚手架
npm i -g @nestjs/cli
# 创建基础项目
nest new node-server-demo 
# 进入项目 
cd new node-server-demo 
# 运行项目测试
npm run start:dev

Let's remove some unnecessary things, start simple and then complex, don't get yourself confused. Next, I will write a simple example to get a feel for this framework, and then I will publish the complete code later. Without further ado, let’s get started! Directory structure after adjustment:

- Public method class common
- Configuration class files config
- Controller, used to handle various requests initiated by the front end controller
- Service class, used to handle interaction logic with the database service
- DTO (Data Transfer Object) can be used to verify input data and limit the fields or formats transferred. dto
- Entity class, used to describe object-related attribute information entities
- Module, used to register all service classes and controller classes, similar to beans in Spring module
◦These are not completely equivalent. The two implementation mechanisms are different. It is just to help everyone understand.
- nest startup portal main.ts
- typescript related declaration types types





I’m just writing a demo, so I don’t have to write comments if I hurry up. I feel that I can understand it at a glance, and it is very consistent with the writing method of Java SpringBoot. Part of the code is shown:

  • controllercontroller
// packages/node-server-demo/src/controller/user/index.ts
import { Controller, Get, Query } from '@nestjs/common';
import UserServices from '@/service/user';
import { GetUserDto, GetUserInfoDto } from '@/dto/user';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserServices) {}

  // Get 请求 user/name?name=bricechou
  @Get('name')
  async findByName(@Query() getUserDto: GetUserDto) {
    return this.userService.read.findByName(getUserDto.name);
  }
 
  // Get 请求 user/info?id=123
  @Get('info')
  async findById(@Query() getUserInfoDto: GetUserInfoDto) {
    const user = await this.userService.read.findById(getUserInfoDto.id);
    return { gender: user.gender, job: user.job };
  }
}

// packages/node-server-demo/src/controller/log/add.ts
import { Controller, Post, Body } from '@nestjs/common';
import { AddLogDto } from '@/dto/log';
import LogServices from '@/service/log';

@Controller('log')
export class CreateLogController {
  constructor(private readonly logServices: LogServices) {}

  // post('/log/add')
  @Post('add')
  create(@Body() createLogDto: AddLogDto) {
    return this.logServices.create.create(createLogDto);
  }
}


  • Data TransferData Transfer Object
// packages/node-server-demo/src/dto/user.ts
export class CreateUserDto {
  name: string;
  age: number;
  gender: string;
  job: string;
}

// 可以分开写,也可以合并
export class GetUserDto {
  id?: number;
  name: string;
}

// 可以分开写,也可以合并
export class GetUserInfoDto {
  id: number;
}


  • service database interaction processing class
// packages/node-server-demo/src/service/user/read.ts
import { Injectable } from '@nestjs/common';
import { User } from '@/entities/User';

@Injectable()
export class ReadUserService {
  constructor() {}

  async findByName(name: string): Promise<User> {
    // 可以处理判空,从数据库读取/写入数据,可能会被多个 controller 进行调用
    console.info('ReadUserService findByName > ', name);
    return Promise.resolve({ id: 1, name, job: '程序员', gender: 1, age: 18 });
  }

  async findById(id: number): Promise<User> {
    console.info('ReadUserService findById > ', id);
    return Promise.resolve({
      id: 1,
      name: 'BriceChou',
      job: '程序员',
      gender: 1,
      age: 18,
    });
  }
}


  • module module registration, service class/control class
// packages/node-server-demo/src/module/user.ts
import { Module } from '@nestjs/common';
import UserService, { ReadUserService } from '@/service/user';
import { UserController } from '@/controller/user';

@Module({
  providers: [UserService, ReadUserService],
  controllers: [UserController],
})
export class UserModule {}


// packages/node-server-demo/src/module/index.ts 根模块注入
import { Module } from '@nestjs/common';
import { UserModule } from './user';
import { LogModule } from './log';

@Module({
  imports: [
    UserModule,
    LogModule,
  ],
})
export class AppModule {}


  • main.js starts all registered classes
// packages/node-server-demo/src/main.ts
import { AppModule } from '@/module';
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  // 监听端口 3000 
  await app.listen(3000);
}

bootstrap();

In this way, a stand-alone server is started. We can use [ https://hoppscotch.io/ ] to make a request and see the return effect.Postwoman





The console has also received the logs. These log requests can be saved as files later, so that the request logs are also available, perfect! Next, we start to connect to the database, so that we don’t have to play in the mud on a single machine~.log

Step 2: Configure MySQL

MySQL installation is actually very simple. My computer is a Mac, so the screenshots below are all based on a Mac. First download the corresponding database.

Download address: https://dev.mysql.com/downloads/mysql/ As for other systems, you can find tutorials online. This one is probably pretty common, so I won’t repeat the tutorial.

  • Note: You must set a password for the installed database, and you must have a password to connect to the database, otherwise the database connection will fail.
  • For MySQL, we only need to install the database. If you are familiar with the instructions, you can just operate it directly from the command line.
  • If you are not familiar with it, then download the graphical management tool.
Mysql official console https://dev.mysql.com/downloads/workbench/

PS: When installing workbench, I found that the requirements are above, and my computer is .MacOS 13MacOS 12

Downloading is in vain, so you can only find the lower version from the archive at https://downloads.mysql.com/archives/workbench/ . There are also version requirements for database services. You can choose the supported version according to your computer version. . What I chose here is the latest version by default: , download it and install it directly , and remember the Root password you entered all the way! ! !8.0.31https://downloads.mysql.com/archives/community/8.0.34Next

Confirm whether the current database is running and start Workbench to check the status.



1.Create database

There is a character set selection in the database. Different character sets and verification rules will have an impact on the stored data, so you can query it by yourself and choose according to your own data storage principles. I choose the broadest one by default. After confirming, select the Apply button in the lower right corner.

2. Create tables and attributes



Answer options:

Is a combination of one or more columns in a table that uniquely identifies each row in the table. PRIMARY KEY
and will not be explained, it is the literal translation. Not NULLUnique
A generated column is a special type of column in a table whose value is not obtained from an insert statement, but is generated by an expression or function based on the values ​​of other columns. GENERATED
CREATE TABLE people (
    first_name VARCHAR(100),
    last_name VARCHAR(100),
    full_name VARCHAR(200) AS (CONCAT(first_name, ' ', last_name))
);

  • UNSIGNEDThis numeric type can only store positive numbers (including zero), not negative numbers.
  • ZEROFILLFilling the front of a numeric type field with zeros will automatically change the field until the field reaches the declared length, such as: 00007UNSIGNED
  • BINARYUsed to store binary strings. If a field is declared as BINARY(5), then the strings stored in this field will be processed as binary strings with a length of 5.
◦If you try to store a string of length 3, it will be padded with two null bytes on the right.
◦If you try to store a string of length 6 then it will be truncated to length 5
◦The main purpose is to store data that needs to be compared on a byte basis, such as cryptographic hashes
  • In addition, you can also easily create an index to facilitate quick search.
CREATE TABLE `rrweb`.`test_sys_req_log` (
  `id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
  `content` TEXT NOT NULL,
  `l_level` INT UNSIGNED NOT NULL,
  `l_category` VARCHAR(255) NOT NULL,
  `l_created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `l_updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE INDEX `id_UNIQUE` (`id` ASC) VISIBLE,
  INDEX `table_index` (`l_level` ASC, `l_category` ASC, `l_time` ASC) VISIBLE);

3. Connect to the database

Since there are currently no official precompiled binaries for the Apple Silicon architecture. As a result, we cannot use the linked database operation on the Mac M1 chip , which currently only supports Mac x86 chips . Alas~ After struggling for a long time and checking various documents, there is this pitfall. It doesn't matter, let's open it in another way.node-oracledbTypeORM

We had to give up and choose https://docs.nestjs.com/techniques/database#sequelize-integration Bang Bang Bang~ The operation is as fierce as a tiger, and it’s done!

  • InstallSequelize
# 安装连接库
npm install --save @nestjs/sequelize sequelize sequelize-typescript mysql2
# 安装 type
npm install --save-dev @types/sequelize

  • Configure basic database information
// packages/node-server-demo/src/module/index.ts
import { Module } from '@nestjs/common';
import { UserModule } from './user';
import { LogModule } from './log';
import { Log } from '@/entities/Log';
import { SequelizeModule } from '@nestjs/sequelize';

@Module({
  imports: [
    SequelizeModule.forRoot({
      dialect: 'mysql',
      // 按数据库实际配置
      host: '127.0.0.1',
      // 按数据库实际配置
      port: 3306,
      // 按数据库实际配置
      username: 'root',
      // 按数据库实际配置
      password: 'hello',
      // 按数据库实际配置
      database: 'world',
      synchronize: true,
      models: [Log],
      autoLoadModels: true,
    }),
    LogModule,
    UserModule,
  ],
})
export class AppModule {}


  • One-to-one mapping between entities and databases
import { getNow } from '@/common/date';
import {
  Model,
  Table,
  Column,
  PrimaryKey,
  DataType,
} from 'sequelize-typescript';

@Table({ tableName: 'test_sys_req_log' })
export class Log extends Model<Log> {
  @PrimaryKey
  @Column({
    type: DataType.INTEGER,
    autoIncrement: true,
    field: 'id',
  })
  id: number;

  @Column({ field: 'content', type: DataType.TEXT })
  content: string;

  @Column({ field: 'l_level', type: DataType.INTEGER })
  level: number; // 3严重,2危险,1轻微

  @Column({ field: 'l_category' })
  category: string; // 模块分类/来源分类

  @Column({
    field: 'l_created_at',
    type: DataType.NOW,
    defaultValue: getNow(),
  })
  createdAt: number;

  @Column({
    field: 'l_updated_at',
    type: DataType.NOW,
    defaultValue: getNow(),
  })
  updatedAt: number;
}


  • module registration entity
// packages/node-server-demo/src/module/log.ts
import { Module } from '@nestjs/common';
import { SequelizeModule } from '@nestjs/sequelize';
import { Log } from '@/entities/Log';
import LogServices, {
  CreateLogService,
  UpdateLogService,
  DeleteLogService,
  ReadLogService,
} from '@/service/log';
import {
  CreateLogController,
  RemoveLogController,
  UpdateLogController,
} from '@/controller/log';

@Module({
  imports: [SequelizeModule.forFeature([Log])],
  providers: [
    LogServices,
    CreateLogService,
    UpdateLogService,
    DeleteLogService,
    ReadLogService,
  ],
  controllers: [CreateLogController, RemoveLogController, UpdateLogController],
})
export class LogModule {}


  • service operates the database to process data
import { Log } from '@/entities/Log';
import { Injectable } from '@nestjs/common';
import { AddLogDto } from '@/dto/log';
import { InjectModel } from '@nestjs/sequelize';
import { ResponseStatus } from '@/types/BaseResponse';
import { getErrRes, getSucVoidRes } from '@/common/response';

@Injectable()
export class CreateLogService {
  constructor(
    @InjectModel(Log)
    private logModel: typeof Log,
  ) {}

  async create(createLogDto: AddLogDto): Promise<ResponseStatus<null>> {
    console.info('CreateLogService create > ', createLogDto);
    const { level = 1, content = '', category = 'INFO' } = createLogDto || {};
    const str = content.trim();
    if (!str) {
      return getErrRes(500, '日志内容为空');
    }
    const item = {
      level,
      category,
      // Tips: 为防止外部数据进行数据注入,我们可以对内容进行 encode 处理。
      // content: encodeURIComponent(str),
      content: str,
    };
    await this.logModel.create(item);
    return getSucVoidRes();
  }
}

The operation was as fierce as a tiger's, and when I looked back, hehehehe~ Finally, we received the first piece of data from the outside world! hello world!





Connection and data creation successful! At this point, the basic functions have been completed~

Step 3: Implement basic functionsCRUD

In fact, everyone can figure out the rest of the content by themselves, which is to call the operating logic of the database. Let’s first talk about whatCRUD

  • Ccreate create
  • Rread read
  • Uupdate update
  • Ddelete delete

Here is a simple example for everyone to take a look at. The rest is just to find the documentation and implement the business logic:

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { User } from './user.model';

@Injectable()
export class UserService {
  constructor(
    @InjectModel(User)
    private userModel: typeof User,
  ) {}
  // 创建新数据
  async create(user: User) {
    const newUser = await this.userModel.create(user);
    return newUser;
  }
  // 查找所有数据
  async findAll() {
    return this.userModel.findAll();
  }
  // 按要求查找单个
  async findOne(id: string) {
    return this.userModel.findOne({ where: { id } });
  }
  // 按要求更新
  async update(id: string, user: User) {
    await this.userModel.update(user, { where: { id } });
    return this.userModel.findOne({ where: { id } });
  }
  // 按要求删除
  async delete(id: string) {
    const user = await this.userModel.findOne({ where: { id } });
    await user.destroy();
  }
}

Tips:When deleting, we can perform fake deletion of two databases, one is the backup database and the other is the main database. The master database can be deleted directly or a flag can be added to indicate deletion. Back up the database, you can only write and update operations without deletion, so that data restoration operations can be performed.

In addition, in order to prevent SQL database injection, everyone needs to uniformly verify the data source or directly encode it. Important data can be directly encrypted with MD5 to prevent the database from being directly downloaded and leaked. Regarding the security processing of SQL databases, there are many online tutorials, just look for them~

The deployment is relatively simple, so we don’t need to go into details one by one. The database can use the cloud database provided by the group, and Nest is an ordinary node deployment.

Author: JD Retail Zhou Mingliang

Source: JD Cloud Developer Community Please indicate the source when reprinting

The author of the open source framework NanUI switched to selling steel, and the project was suspended. The first free list in the Apple App Store is the pornographic software TypeScript. It has just become popular, why do the big guys start to abandon it? TIOBE October list: Java has the biggest decline, C# is approaching Java Rust 1.73.0 Released A man was encouraged by his AI girlfriend to assassinate the Queen of England and was sentenced to nine years in prison Qt 6.6 officially released Reuters: RISC-V technology becomes the key to the Sino-US technology war New battlefield RISC-V: Not controlled by any single company or country, Lenovo plans to launch Android PC
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10116557