Nest+TypeORM助你成为1%全栈工程师

发布时间 2023-09-28 16:50:19作者: 漫思

Nest+TypeORM助你成为1%全栈工程师

 

1%全栈工程师

在技术圈子里, 一个人若是自称全栈, 往往会遇到以下两种情况.

如果前端自称全栈, 那么请回答:

  • 如何实现LRU缓存机制?
  • MySQL的数据如何恢复到任意时间点?
  • 使用Redis有哪些好处?
  • 存储过程与触发器的区别?
  • JVM垃圾收集器有哪些?谈谈优劣势比较?

如果后端自称全栈, 那么请回答:

  • 元素浮动有哪些缺陷?
  • 如何用纯CSS实现一个三角形?
  • 如何实现寄生组合继承?
  • 谈谈JS的事件循环?
  • AMD与CMD的区别是什么?

为了避免被dis, 本篇文章的目标设定为成为1%全栈工程师. 我就会1%, 这些题我答不上来不是很正常么?

通识技能

作为前端你可能不了解分布式、乐观锁悲观锁、 数据存储等细节; 作为后端你可能不了解JS的特性、 CSS3属性、 浏览器兼容. 但是有一些技能是通识性技能.

  • 关系型数据库有主键、外键的概念知道吧?
  • 数据库基本操作CRUD知道吧?
  • 继承的概念知道吧?
  • 数据库标识往往用id, 且通常是自增知道吧?
  • 网页应用数据都是从[前端→后端→数据库→前端]知道吧?
  • ...

本文默认你已经掌握了这些学生时代就讲过的知识. 前端所谓的全栈, 就是用JS实现后端. 这些通识认知是最基本的要求.

Nest

我作为一个切图仔, 既不会Java那一套, 也不会C#那一套. 日常工作我也只用JS. 我能写后端吗?

能! 使用Nest, 可以让你傻瓜式开发服务端.

只需三步, 就可以让你的服务端跑起来.

第一步: 安装环境

npm i -g @nestjs/cli

第二步: 初始化项目

nest new project-name

第三步: 安装依赖包并运行

npm install
npm run start

发送Get请求

很常规的操作吧? 现在我们看下初始化项目中代码都有啥.

app.controller.ts

import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello();
  }
}

app.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

我相信这两段代码并不需要我额外的解释, 你一看就明白发生了什么并且能够预判请求会发生什么.

Postman请求

不过别小看这两段代码, 他体现了最基本的编程思想, 将控制器与数据处理相分离.

可能看到这里各位前端工程师会有疑惑: 不对啊, 我平时看到的请求没那么简单粗暴啊, 以用户模块为例, 怎么着也得是getUser?id=1这样的, 你这啥都没的, 太糙了吧.

No problem! 改造下控制器代码

发送带参数的Get请求

@Controller('user')
export class AppController {
  @Get('getUser')
  getUser(@Query() query): string {
    return `查找到用户编号为:${query.id}`;
  }
}
Postman请求

怎么样? 是不是好像有点样子了? Nest最神奇的地方就是代码一看就懂. 需要我告诉你这段代码怎么接收参数怎么设置路由么? 如果需要的话, 我可以给你介绍一些口碑不错的眼科医院. .♪(^∀^●)ノ

发送Post请求

最后写一个Post请求来结束Nest部分的内容吧.

@Controller('user')
export class AppController {
  @Post('login')
  userLogin(@Body() user: { account: string, password: string }): string {
    if (user.account === 'admin' && user.password === '12345') {
      return '登陆成功';
    } else {
      return '登陆失败';
    }
  }
}

可以说, 如果学生时代你写过JSP的作业, 你就会写Nest. 顺便一提, Nest是支持typescript的, 书写体验非常棒. 更重要的是, 其作者受到Angular的启发, 语法和Angular非常像, 同样有module, pipe, providers这些概念, 不展开介绍了, 官方文档写的已经很详细了, 感兴趣可以自行前往.

各位大神如果一定要纠结Node的性能、并发等问题. 请注意, 我只是一个1%全栈工程师, 要啥自行车呢?

TypeORM

我就是个切图仔, 数据库查询语句我只知道一个select * from table. 可我就是想搞数据库. 有办法吗?

有! TypeORM就非常适合咱们这种没什么本事野心还贼大的菜b.

对象关系映射(Object Relational Mapping), 简称ORM. 简单地来说, 就是数据库中表与表的关系, 和实体类的关系非常像, 可以写好实体然后映射过去. 话不多说, 开门见山.

在前文Nest项目的基础上继续安装依赖. 数据库我用的是mysql, 如果是其他数据库请参考文末TypeORM官网.

npm install @nestjs/typeorm --save
npm install mysql --save
npm install typeorm --save

声明实体类

user.entity.ts

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    account: string;

    @Column()
    password: string;

    @CreateDateColumn()
    createTime: Date;

    @UpdateDateColumn()
    updateTime: Date;
}

user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>
  ) { }

  async save(user: User): Promise<User> {
    return this.userRepository.save(user);
  }
}

user.controller.ts

import { Controller, Post, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './user.entity';

@Controller('user')
export class UserController {
  constructor(
    private readonly service: UserService
  ) { }

  @Post('save')
  async save(@Body() dto: User): Promise<any> {
    let message = '';
    await this.service.save(dto).then(() => {
      message = '注册成功';
    }).catch(e => {
      message = '注册失败';
    });
    return message;
  }
}

user.module.ts

@Module({
    imports: [TypeOrmModule.forFeature([User])], // 引入实体类
    providers: [UserService], // 为服务提供注册商
    controllers: [UserController], // 控制器
})
export class UserModule { }

配置ORM

app.module.ts

@Module({
  imports: [
    UserModule,
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: '12345',
      database: 'demo', // 自己提前建好数据库, 无需建表
      entities: ['src/**/**.entity{.ts,.js}'], // 实体存放的目录, 目前只能靠文件后缀识别
      synchronize: true, // 项目一运行就根据实体自动创建表结构
    }),
  ]
})
export class AppModule { }

简单地分析下, 看代码盲猜结果: 数据请求进入控制器(UserController), 解析请求数据并调用service进行数据存储, 返回响应. 代码看上去好像是这么运行的, 那我们来验证一下.

先开启服务, 直接npm start. 此时观察我们的数据库可以发现, user表已经自己创建好了.

接下来我们尝试发送请求

前往数据库查看

这里有要你写SQL语句么? 没有. 但是你的确实现了从前端到后端到数据库操作的流程. 既然在这里我们用到了save方法.

this.userRepository.save(user);

想必你肯定猜到TypeORM提供了delete、find方法等. 本文不负责API搬运, 只介绍基本概念和初始化操作, 属于科普向文章. 更多细节请查看TypeORM官网.

现在, 使用JS的你, 可以写前端页面, 也可以写后端逻辑, 还能进行数据存储. 作为一名JSer, 真开心.

2019, 重新出发, 从心出发

记得高考的时候, 就流传着一种说法:高考的魅力不在于如愿以偿,而在于阴差阳错.

活的愈久, 愈觉得很多结果是无法左右的. 能够随遇而安, 便是我力所能及最积极的事儿了.

因机缘巧合入职上海轻流, 工作已经四个月了, 学到了不少工作流程上的事情. 感谢2019年的阴差阳错能够让我遇到一群如此有趣的同事, 非常开心能够和你们共事.

我也在努力地成为1%全栈工程师, 争取能够用这些工具实现自己的一些小想法.顺便预祝我们公司的BPM产品[轻流]两周年快乐!!!