实现一个 TodoList 应用
- UI实现
- 项目解析
- config 异常,管道,中间件,过滤器 使用
- todo 主要实现todolist的增删改查
- app.module.ts 组织应用程序结构
- main.ts 项目配置文件,监听端口
实现
创建文件
在 cli 项目的基础上,创建todo 文件夹,如上,创建一些文件,可以手动创建,当然,也可以使用nest的命令。
generate(简写:g) 生成文件
- class (简写: cl) 类
- controller (简写: co) 控制器
- decorator (简写: d) 装饰器
- exception (简写: e) 异常捕获
- filter (简写: f) 过滤器
- gateway (简写: ga) 网关
- guard (简写: gu) 守卫
- interceptor (简写: i) 拦截器
- middleware (简写: mi) 中间件
- module (简写: mo) 模块
- pipe (简写: pi) 管道
- provider (简写: pr) 供应商
- service (简写: s) 服务
创建一个users服务文件
$ nest generate service users
OR
$ nest g s users
注意:
- 必须在项目根目录下创建,(默认创建在src/)。(不能在当前文件夹里面创建,不然会自动生成xxx/src/xxx。吐槽:这个没有Angular-cli智能)
- 需要优先新建模块,不然创建的非模块以外的服务,控制器等就会自动注入更新到上级的模块里面
引入相关
既然是后端服务,那当然是要连接数据库了。我们使用 mysql数据库,然后使用typeorm来操作数据库,nest中有内置相关类 @nest/typeorm
$ npm install --save @nestjs/typeorm typeorm mysql
两种方式 引入
- 将其 TypeOrmModule 导入到根目录中 ApplicationModule
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'root',
database: 'test',
entities: [__dirname + '/../**/*.entity{.ts,.js}'],
synchronize: true,
}),
],
})
export class ApplicationModule {}
- 在项目根目录中创建一个 ormconfig.json 文件,会自动识别
ormconfig.json
{
"type": "mysql",
"host": "localhost",
"port": 3306,
"username": "root",
"password": "root",
"database": "test",
"entities": ["src/**/**.entity{.ts,.js}"],
"synchronize": true
}
app.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [TypeOrmModule.forRoot()],
})
export class ApplicationModule {}
业务逻辑
我们想要实现的是一个 todoList 主要操作有 增删改查
设计数据库
在上面的ormconfig.json
中 database: test
是表示 所要连接的数据库。是需要本地先新建好的。如果没有,就会报错。推荐使用 Navicat 可视化数据库表,更好操作。
还有,本地数据库的 用户名,密码都是要在上面设置好的,要统一。
然后新建一个实体,运行之后会在 test 数据库中 生成一个相关的表
todo.entity.ts
import {Column, Entity, PrimaryGeneratedColumn} from "typeorm";
@Entity("t_todo")
export class t_todo {
// 主键 id
@PrimaryGeneratedColumn({
type:"bigint",
name:"id"
})
id:number;
// summary
@Column("varchar",{
nullable:true,
length:200,
name:"summary"
})
// 这个字段就是输出给前端时的字段,在这里就可以写成 驼峰式的
summary:string | null;
// details
@Column("text",{
nullable:true,
name:"details"
})
details:string | null;
// 是否已完成
@Column("int",{
nullable:true,
name:"is_finished"
})
is_finished:number | null;
// 是否已删除
@Column("int",{
nullable:true,
name:"is_del"
})
is_del:number | null;
// 创建时间
@Column("timestamp",{
nullable:true,
default: () => "CURRENT_TIMESTAMP",
name:"create_time"
})
createTime:Date | null;
// 更新时间
@Column("timestamp",{
nullable:true,
default: () => "CURRENT_TIMESTAMP",
name:"update_time"
})
updateTime:Date | null;
}
基本的表结构就是这样,然后在 todo.module.ts中导入这个实体
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TodoController } from './todo.controller';
import { TodoService } from './todo.service';
import { t_todo } from './todo.entity'
@Module({
imports: [TypeOrmModule.forFeature([t_todo])],
controllers: [TodoController],
providers: [TodoService],
exports: [TodoService]
})
export class TodoModule {}
然后 npm run start:dev
来生成表
这样表结构就生成了,接着就是逻辑实现了。
数据传输对象
后端写接口,一定要控制好传入params的数据格式,所以要对操作数据库时数据做类型校验,在ts中推荐使用 DTO(数据传输对象)。
新建一个 todo.dto.ts
export class todo {
summary: string
details?: string
is_finished?: number
is_del?: number
id?: number
}
路由
接下来可以写请求了,在controller中实现
主要是抛出给前端使用的接口,在这里调用服务的方法。
todo.controller.ts
import { Controller, Get, Post, Put, Body, Delete, Param } from '@nestjs/common';
import { TodoService } from './todo.service';
import { todo } from './todo.dto';
@Controller('todo')
export class TodoController {
// 初始化服务
constructor(
private readonly todoService: TodoService
){ }
@Get()
root() {
return this.todoService.root();
}
// 查全部
@Get()
getTodo() {
return this.todoService.getTodo();
}
// 查一个
@Get(':id')
findOne(@Param('id') id: number) {
return this.todoService.findOne(id)
}
// 改
@Post('update')
update(@Body() todo:todo) {
return this.todoService.update(todo)
}
// 删
@Post('delete')
delete(@Body() id:number) {
return this.todoService.delete(id)
}
// 增
@Post('create')
create(@Body() todo: todo): Promise<todo> {
return this.todoService.create(todo)
}
//标记已完成
@Post('finish')
finish(@Body() id:number) {
console.log('aaa');
return this.todoService.finish(id)
}
}
@nestjs/common
中封装了很多装饰器,可以拿来即用。这里只涉及简单的request的使用,了解更多
@Controller('todo')
表示接口输出的路径,在这个下面写的所有接口都是基于 /todo/ 的。
服务
todo.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { t_todo as TodoEntity } from './todo.entity';
import { todo } from './todo.dto';
import { Repository } from 'typeorm';
@Injectable()
export class TodoService {
// 注入相关数据库
// @InjectRepository(TodoEntity)
private readonly todoEntity: Repository<TodoEntity>
root(): string {
return 'Hello World!';
}
// 查全部
async getTodo(): Promise<todo[]> {
return await this.todoEntity.find();
}
// 查一个
async findOne(id: number): Promise<todo> {
return await this.todoEntity.findOne({id:id})
}
// 增
async create(todo: todo): Promise<todo> {
const res = this.todoEntity.create(todo)
const result = await this.todoEntity.save(res)
return result;
}
//删
async delete(id:number) {
return await this.todoEntity.delete(id);
}
// 更新
async update(todo: todo) {
return await this.todoEntity.update({id: todo.id}, {...todo})
}
// 标记为已完成
async finish(id:number) {
return await this.todoEntity.update(id, {is_finished:1})
}
}
Repository 提供了很多可以直接使用的操作数据表的方法。比如 find()
,findOne()
,save()
,update()
,delete()
等
基本实现就先这样。