丹阳网站建设服务,wordpress登录界面背景图片,可以建公司网站,手机广告推广软件前言沉默了很久#xff0c;一直都没发文章#xff0c;有些惭愧。最近实习结束之后回了学校#xff0c;提前开始做毕业设计了。对#xff0c;就是毕业设计。近两个月把 React Native、Vue 3.0 和 Nest.js 都摸了一下#xff0c;大概都摸懂了。鉴于掘金已经很多优秀的 Vue 3…前言沉默了很久一直都没发文章有些惭愧。最近实习结束之后回了学校提前开始做毕业设计了。对就是毕业设计。近两个月把 React Native、Vue 3.0 和 Nest.js 都摸了一下大概都摸懂了。鉴于掘金已经很多优秀的 Vue 3.0 教程了本人自认为文笔远逊于掘金的大佬们就没有班门弄斧也写一篇了本来很想了后来想想感觉炒冷饭没啥意思如果有想看我的教程风格的同学可以点赞或者在评论区里留言说一下也不是不能写。很多后端的同学都说你们前端不就是切个图嘛凭啥跟我们后端的同学平起平坐啊这下前端的同学可以站起来了你们后端不也就是 CRUD 嘛凭啥瞧不起我们前端的同学啊今天就写一下最近做毕业设计用到的框架Nest.js 的基础教程吧简单教大家做一下 CRUD小白向大佬轻喷。挖个坑这应该是最基础的第一章吧如果大家觉得好就多点赞评论过 200 点赞就加更一些争取让大家从前端走向全栈吧。这个教程的所有代码我都放在了我的 GitHub 仓库Nest-CRUD-Demo欢迎大家点个 Star同时也欢迎大家关注 「Hello FE」里面有非常多其他的精品好文不论是还在学习前端的同学还是已经工作了一段时间的朋友都可以阅读一下关注还有小惊喜链接过期了可以在后台回复我看到了会回复新的链接。框架简介Nest 是一个用于构建高效可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript内置并完全支持 TypeScript但仍然允许开发人员使用纯 JavaScript 编写代码并结合了 OOP面向对象编程FP函数式编程和 FRP函数式响应编程的元素。 在底层Nest 使用强大的 HTTP Server 框架如 Express默认和 Fastify。Nest 在这些框架之上提供了一定程度的抽象同时也将其 API 直接暴露给开发人员。这样可以轻松使用每个平台的无数第三方模块。我猜肯定很多同学看不懂这段话没关系我也暂时看不懂但这不影响我们学会用它 CRUD。我们只需要知道它是一款 Node.js 的后端框架规范化和开箱即用的特性使其在国外开发者社区非常流行社区也非常活跃GitHub Repo 拥有 31.1k Star。相比于 Express 和 Koa 的千奇百怪五花八门Nest 确实是一股清流。不过我们国内也有很棒的 Node.js 框架比如说 Midway和 Nest 一样采用的 IoC 的机制想了解一下的同学可以看我的小伙伴林不渡写的文章《走近 MidwayJS 初识 TS 装饰器与 IoC 机制》还可以到 Midway 官网自行探索。包括在 Nest 当中遇到的装饰器相关的知识大家也可以到上面林不渡同学的那篇文章中了解。前置知识HTTPTypeScript/JavaScript项目环境gitmongodbnode.js 10.13.0安装 MongoDB这个章节的教程我就只写 Mac OS 上的安装了毕竟上了大学就很少用 Windows 了用 Windows 的同学可以到 https://mongodb.com/download-center/communityMongoDB 官网选择对应的系统版本去下载 msi 的安装包或者搜索引擎里搜索一下记得限定一下结果的时间保证能够搜索到最新的教程。强烈建议使用 Homebrew 来对 Mac OS 的软件包环境进行管理没有安装的同学可以点击这里下载。由于目前 MongoDB 已经不开源了因此我们想要安装 MongoDB 就只能安装社区版本。brew tap mongodb/brew
brew install mongodb-community安装好之后我们就可以启动 MongoDB 的服务了brew services start mongodb-community服务启动了就不用管了如果要关闭的话可以把 start 改成 stop就能够停止 MongoDB 的服务了。构建项目有两种方式可以自行选择两者没有区别使用 Nest CLI 安装npm i -g nestjs/cli
nest new nest-crud-demo使用 Git 安装git clone https://github.com/nestjs/typescript-starter.git nest-crud-demo这两条命令的效果完全一致就是初始化一个 Nest.js 的项目到当前文件夹下项目的文件夹名字为 nest-crud-demo两种方式都可以。当然我还是建议采用第一种方式因为后面我们可以直接使用脚手架工具生成项目文件。启动服务cd nest-crud-demo
npm run start:dev 或者 yarn run start:dev就可以以开发模式启动我们的项目了。这里其实有一个小小的点就是启动的时候应该以 dev 模式启动这样 Nest 会自动检测我们的文件变化然后自动重启服务。如果是直接 npm start 或者 yarn start 的话虽然服务启动了但是我们如果在开发的过程中修改了文件就要手动停止服务然后重新启动效率挺低的。安装依赖项目中我们会用到 Mongoose 来操作我们的数据库Nest 官方为我们提供了一个 Mongoose 的封装我们需要安装 mongoose 和 nestjs/mongoosenpm install mongoose nestjs/mongoose --save安装好之后我们就可以开始编码过程了。编写代码创建 Module我们这次就创建一个 User 模块写一个用户增删改查带大家熟悉一下这个过程。nest g module user server脚手架工具会自动在 src/server/user 文件夹下创建一个 user.module.ts这是 Nest 的模块文件Nest 用它来组织整个应用程序的结构。// user.module.ts
import { Module } from nestjs/common;Module({})
export class UserModule {}
同时还会在根模块 app.module.ts 中引入 UserModule 这个模块相当于一个树形结构在根模块中引入了 User 模块。执行上面的终端命令之后我们会惊讶地发现app.module.ts 中的代码已经发生了变化在文件顶部自动引入了 UserModule同时也在 Module 装饰器的 imports 中引入了 UserModule。// app.module.ts
import { Module } from nestjs/common;
import { AppController } from ./app.controller;
import { AppService } from ./app.service;
import { UserModule } from ./server/user/user.module; // 自动引入Module({imports: [UserModule], // 自动引入controllers: [AppController],providers: [AppService]
})
export class AppModule {}
创建 Controllernest g controller user server在 Nest 中controller 就类似前端的路由负责处理客户端传入的请求和服务端返回的响应。举个例子我们如果要通过 http://localhost:3000/user/users 获取所有的用户信息那么我们可以在 UserController 中创建一个 GET 方法路径为 users 的路由这个路由负责返回所有的用户信息。// user.controller.ts
import { Controller, Get } from nestjs/common;Controller(user)
export class UserController {Get(users)findAll(): string {return All Users Info; // [All Users Info] 暂时代替所有用户的信息}
}
这就是 controller 的作用负责分发和处理请求和响应。当然也可以把 findAll 方法写成异步方法像这样// user.controller.ts
import { Controller, Get } from nestjs/common;Controller(user)
export class UserController {Get(users)async findAll(): Promiseany {return await this.xxx.xxx(); // 一些异步操作}
}
创建 Providernest g service user serverprovider 我们可以简单地从字面意思来理解就是服务的提供者。怎么去理解这个服务提供者呢举个例子我们的 controller 接收到了一个用户的查询请求我们不能直接在 controller 中去查询数据库并返回而是要将查询请求交给 provider 来处理这里我们创建了一个 UserService就是用来提供数据库操作服务的。// user.service.ts
import { Injectable } from nestjs/common;Injectable()
export class UserService {}
当然provider 不一定只能用来提供数据库的操作服务还可以用来做一些用户校验比如使用 JWT 对用户权限进行校验的策略就可以写成一个策略类放到 provider 中为模块提供相应的服务。挺多文档将 controller 和 provider 翻译为控制器和提供者我感觉这种翻译挺生硬的让人不知所云所以我们姑且记忆他们的英文名吧。controller 和 provider 都创建完后我们又会惊奇地发现user.module.ts 文件中多了一些代码变成了这样// user.module.ts
import { Module } from nestjs/common;
import { UserController } from ./user.controller;
import { UserService } from ./user.service;Module({controllers: [UserController],providers: [UserService]
})
export class UserModule {}
从这里开始我们就要开始用到数据库了~连接数据库引入 Mongoose 根模块连接数据之前我们要先在根模块也就是 app.module.ts 中引入 Mongoose 的连接模块// app.module.ts
import { Module } from nestjs/common;
import { MongooseModule } from nestjs/mongoose;
import { AppController } from ./app.controller;
import { AppService } from ./app.service;
import { UserModule } from ./server/user/user.module;Module({imports: [MongooseModule.forRoot(mongodb://localhost/xxx), UserModule],controllers: [AppController],providers: [AppService]
})
export class AppModule {}
这段代码里面的 mongodb://localhost/xxx 其实就是本地数据库的地址xxx 是数据库的名字。这时候保存文件肯定有同学会发现控制台还是报错的我们看一下报错信息就很容易知道问题在哪里了。其实就是 mongoose 模块没有类型声明文件这就很容易解决了安装一下就好npm install types/mongoose --dev 或者 yarn add types/mongoose --dev安装完之后服务就正常重启了。引入 Mongoose 分模块这里我们先要创建一个数据表的格式在 src/server/user 文件夹下创建一个 user.schema.ts 文件定义一个数据表的格式// user.schema.ts
import { Schema } from mongoose;export const userSchema new Schema({_id: { type: String, required: true }, // 覆盖 Mongoose 生成的默认 _iduser_name: { type: String, required: true },password: { type: String, required: true }
});
然后将我们的 user.module.ts 文件修改成这样// user.module.ts
import { Module } from nestjs/common;
import { MongooseModule } from nestjs/mongoose;
import { UserController } from ./user.controller;
import { userSchema } from ./user.schema;
import { UserService } from ./user.service;Module({imports: [MongooseModule.forFeature([{ name: Users, schema: userSchema }])],controllers: [UserController],providers: [UserService]
})
export class UserModule {}
好了现在一切就绪终于可以开始编写我们的 CRUD 逻辑了冲冲冲~CRUD我们打开 user.service.ts 文件为 UserService 类添加一个构造函数让其在实例化的时候能够接收到数据库 Model这样才能在类中的方法里操作数据库。// user.service.ts
import { Injectable } from nestjs/common;
import { InjectModel } from nestjs/mongoose;
import { Model } from mongoose;
import { CreateUserDTO } from ./user.dto;
import { User } from ./user.interface;Injectable()
export class UserService {constructor(InjectModel(Users) private readonly userModel: ModelUser) {}// 查找所有用户async findAll(): PromiseUser[] {const users await this.userModel.find();return users;}// 查找单个用户async findOne(_id: string): PromiseUser {return await this.userModel.findById(_id);}// 添加单个用户async addOne(body: CreateUserDTO): Promisevoid {await this.userModel.create(body);}// 编辑单个用户async editOne(_id: string, body: EditUserDTO): Promisevoid {await this.userModel.findByIdAndUpdate(_id, body);}// 删除单个用户async deleteOne(_id: string): Promisevoid {await this.userModel.findByIdAndDelete(_id);}
}
因为 mongoose 操作数据库其实是异步的所以这里我们使用 async 函数来处理异步的过程。好奇的同学会发现这里突然出现了两个文件一个是 user.interface.ts另一个是 user.dto.ts我们现在来创建一下// user.interface.ts
import { Document } from mongoose;export interface User extends Document {readonly _id: string;readonly user_name: string;readonly password: string;
}
// user.dto.ts
export class CreateUserDTO {readonly _id: string;readonly user_name: string;readonly password: string;
}export class EditUserDTO {readonly user_name: string;readonly password: string;
}
其实就是对数据类型做了一个定义。现在我们可以到 user.controller.ts 中设置路由了将客户端的请求进行处理调用相应的服务实现相应的功能// user.controller.ts
import {Body,Controller,Delete,Get,Param,Post,Put
} from nestjs/common;
import { CreateUserDTO, EditUserDTO } from ./user.dto;
import { User } from ./user.interface;
import { UserService } from ./user.service;interface UserResponseT unknown {code: number;data?: T;message: string;
}Controller(user)
export class UserController {constructor(private readonly userService: UserService) {}// GET /user/usersGet(users)async findAll(): PromiseUserResponseUser[] {return {code: 200,data: await this.userService.findAll(),message: Success.};}// GET /user/:_idGet(:_id)async findOne(Param(_id) _id: string): PromiseUserResponseUser {return {code: 200,data: await this.userService.findOne(_id),message: Success.};}// POST /userPost()async addOne(Body() body: CreateUserDTO): PromiseUserResponse {await this.userService.addOne(body);return {code: 200,message: Success.};}// PUT /user/:_idPut(:_id)async editOne(Param(_id) _id: string,Body() body: EditUserDTO): PromiseUserResponse {await this.userService.editOne(_id, body);return {code: 200,message: Success.};}// DELETE /user/:_idDelete(:_id)async deleteOne(Param(_id) _id: string): PromiseUserResponse {await this.userService.deleteOne(_id);return {code: 200,message: Success.};}
}
至此我们就完成了一个完整的 CRUD 操作接下来我们来测试一下~接口测试接口测试我们用的是 Postman大家可以去下载一个非常好用的接口自测工具。数据库可视化工具我们用的是 MongoDB 官方的 MongoDB Compass也很不错。GET /user/users一开始我们的数据库中什么都没有所以返回了一个空数组没用用户信息。POST /user现在我们添加一条用户信息服务器返回添加成功。GET /user/:_id添加完一条用户信息之后再查询可算是能查询到我的信息了。PUT /user/:_id现在假如我想修改密码发送一个 PUT 请求。DELETE /user/:_id现在我们删除一下刚才添加的用户信息。会发现数据库中的内容已经被删除了。完结撒花大功告成CRUD 就这么简单用这个项目去参加一些学校举行的比赛拿个奖肯定没什么问题开箱即用学校老师们别打我。总结教程还算是用了比较通俗易懂的方式为大家讲解了如何写一个带有 CRUD 功能的后端 Node.js 应用框架采用的是 Nest.js。相信大家在上面的教程中肯定有非常多不懂的部分比如说 Get()、Post()、Param()、Body() 等等的装饰器再比如说一些 Nest.js 相关的概念。没关系我的建议是学编程先模仿遇到不懂的地方先记住等到自己的积累够多了总有一天你会回过头发现自己茅塞顿开突然懂了。这也是我个人学习的一个小技巧。在学习的过程中也一定会遇到一些问题学习编程的过程中遇到问题不能自己憋着一定要学会请教大佬一定要学会请教大佬一定要学会请教大佬重要的事情说三遍。不过也别很简单的问题就去请教大佬而且最好给一点小小的报酬毕竟谁也没有义务帮你解决问题。我在学习的过程中也请教了一些社区里面的大佬同时还进入了 Nest.js 的社区答疑群向国外友人请教学到了不少知识。当然这个 Demo 中也有很多可以完善的地方比如说错误处理。数据库的操作肯定是有可能出现错误的比如说我们漏传了 required: true 的参数数据库就会报错。这个时候我们就要写一个 try/catch 捕获这个异常或者干脆写一个异常的过滤器将所有的异常统一处理Nest.js 支持过滤器除此之外既然有可能出现异常那么我们就需要一个日志系统去捕获这个异常方便查错纠错。如果涉及到登录注册的部分还有密码加解密的过程同时还可能有权限校验问题需要进行处理。所以后端的同学肯定不止 CRUD 啦可算圆回来了。这个教程的所有代码我都放在了我的 GitHub 仓库Nest-CRUD-Demo欢迎大家点个 Star参考资料NestJS - A progressive Node.js frameworkNest.js 中文文档