前言
哈喽,小伙伴你好,我是 SuperYing,前端开发全栈化趋势 卷的前端小伙伴们日益焦虑,不会搭建后端服务怎么办;不了解 node
应用怎么办;不会组织业务接口怎么办;不用怕!今天我们一起来梳理下 nodejs
框架 – express
搭建后端的全流程,一次性整明白这玩意儿。
不了解
express
的小伙伴,可以看我之前的文章《express 基础入门》。
看完本文您将收获:
- 了解
express
后端工程搭建全流程。- 了解
express
如何集成typescript
,并运行项目。- 了解注解式接口开发,集成
routing-controller
和typeorm
。- 了解
typeorm
如何应用mysql
服务。- 了解
express
业务接口开发方式,明确所需要素,包括controlelr
,entity
,service
等。
技术准备
本文工程基于 express
框架搭建,由 typescript
提供类型支持,ORM
框架选择 typeorm
,应用其特性开发使用 mysql
数据库的应用。
项目涉及的主要依赖:
express
:express
包。typescript
:typescript
包。typeorm
:typeorm
包。body-parser
: 用于设置express
如何处理客户端发送的body
。mysql
/mysql2
: 底层数据库驱动程序。如果你使用的是其他数据库系统,则必须安装相应的包。reflect-metadata
: 若使用装饰器语法则必需,用于使装饰器正常工作。routing-controllers
: 支持以注解的形式处理请求行为,如@Get
,@Post
等。
搭建过程
此过程中涉及到的命令均在 终端 执行。
mac
系统command + 空格
,搜索teminal
或 终端 并打开。
window
系统win + R
,输入cmd
并回车。打开 终端 后,
cd
命令跳转到指定工作空间,就可以开始后面的操作啦。
pnpm
初始化
1. 新增项目目录,目录名为 server
。
# 创建项目目录
mkdir server
# 跳转到工程目录
cd server
2. 执行 pnpm
命令,初始化工程目录
pnpm init
需要全局安装
pnpm
,终端执行npm i pnpm -g
即可。若不喜欢
pnpm
,使用npm
或yarn
也是可以的。
执行完成后,会在 server
根目录下生成一个最基础的 package.json
文件。
{
"name": "server",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
到此,pnpm
初始化完成。
typescript
初始化及配置
1. 安装 typescript
依赖
pnpm add typescript -D
2. 初始化 typescript
配置
tsc --init
执行成功后,会在 server
根目录下生成一个包含默认配置的 tsconfig.json
文件。
3. 根据项目需求,精简配置
typescript
配置文件保留以下配置项:
{
"compilerOptions": {
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
"lib": ["es5", "es6"], /* Specify library files to be included in the compilation. */
"strict": true, /* Enable all strict type-checking options. */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
}
}
注意:需要开启 experimentalDecorators
和 emitDecoratorMetadata
配置,因为后续的 typeorm
和 routing-controller
处理会用拿到 typescript
的装饰器特性。
到此,typescript
相关处理完成。
添加 express
1. 安装 express
相关依赖
pnpm add express body-parser routing-controllers
pnpm add @types/express @types/body-parser -D
2. 添加应用入口文件
2.1 在 server
根目录下,新建 app.ts
文件,作为应用的入口文件。
app.ts
中引用 routing-controllers
的 createExpressServer
函数,用于创建 express
应用实例
import { createExpressServer } from 'routing-controllers'
import { json, urlencoded } from 'body-parser'
const app = createExpressServer({
controllers: []
})
// body 解析相关中间件
// 解析 json 格式
app.use(json())
// 解析 urlencoded body
// 会在 request 对象上挂载 body 属性,包含解析后的数据。
// 这个新的 body 对象包含 key-value 键值对,若设置 extended 为 true,则键值可以是任意累心个,否则只能是字符串或数组。
app.use(urlencoded({ extended: true }))
此时我们还没有 controllers
,将在下一步实现 User
接口时添加。
2.2 启动服务,监听 3000
端口
app.ts
中添加以下代码,表示启动服务,监听 3000
端口,若启动成功则控制台打印提示信息。
app.listen(3000, () => {
console.log(` App is running at http://localhost:3000\n`)
console.log(' Press CTRL-C to stop\n')
})
2.3 测试运行
因为使用 typescript
编写,若想直接运行 app.ts
,需要安装 ts-node
包。
pnpm add ts-node -D
安装完成后,在 package.json
中 scripts
配置处添加 start
启动命令。
"scripts": {
"start": "ts-node app.ts"
},
保存后,控制台运行 pnpm start
,打印如下内容即表示服务启动成功。
打开浏览器访问 http://localhost:3000
:
什么都没有?不要慌,我们还没开发接口,后续我们会实现一个 User
接口。
添加 typeorm
1. 安装 typeorm
相关依赖
pnpm add typeorm mysql reflect-metadata
注意:
后续启动服务时,若出现因本地mysql
版本过低无法授权的问题(Client does not support authentication protocol requested by server; consider upgrading MySQL client),可将mysql
包改为安装mysql2
,即pnpm add typeorm mysql2 reflect-metadata
.
2. 添加数据库配置
在本项目中,数据库使用 mysql
,需要先在本地安装数据库环境(有线上环境就请忽略)。不清楚的小伙伴可以自行百度,资源很多。如果你恰巧使用的 mac
电脑,可以看看我之前的文章《Mac 使用 Mysql》,按步操作即可。
安装并启动了 mysql
环境,就可以开始在项目中配置数据库信息啦。
在 server
根目录新建 data-source.ts
文件,导出一个 DataSource
实例:
import path from 'path'
import { DataSource } from 'typeorm'
const dataSource = new DataSource({
type: 'mysql', // 数据库类型
host: 'localhost', // mysql 服务地址
port: 3306, // mysql 服务启动端口
username: 'root', // mysql 用户名
password: 'admin0125', // mysql 密码
database: 'zimuadmin', // 数据库
entities: [path.join(__dirname, '/../**/*.entity.{js,ts}')], // typeorm 实体
entityPrefix: 'zm-', // 数据库表前缀
logging: true // 开启日志
})
export default dataSource
type
: 数据库类型,比如我用的是mysql
。host
: 数据库服务器地址,本地的都是localhost
。port
: 数据库服务启动端口,默认3306
。username
: 数据库服务登录用户名。password
: 数据库服务登录密码。database
: 指定使用的数据库名称,一个mysql
服务可以包含多个数据库,可以在控制台执行show databases
查询所有的数据库。zimuadmin
是我本地创建的数据库。entities
: 配置typeorm
实体,src/models/*.ts
只src/models
下的ts
文件都是实体。
3. 实现业务接口
到这,我们需要安装的依赖包已经基本搞定啦。接下来,我们来实践一下,开发一个 用户管理模块。
3.1 建表并初始化数据
我们按照正常的开发流程来走,先设计一下用户表结构。大家可以先想一想,用户数据会涉及到哪些属性?
mysql
控制台执行以下 sql
语句:
CREATE TABLE `zm-user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`job_no` varchar(100) NOT NULL COMMENT '工号',
`name` varchar(100) DEFAULT NULL COMMENT '姓名',
`sex` varchar(1) DEFAULT NULL COMMENT '性别',
`tel` varchar(20) DEFAULT NULL COMMENT '手机号',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
`address` varchar(200) DEFAULT NULL COMMENT '地址',
`status` varchar(50) DEFAULT NULL COMMENT '状态',
`is_super` varchar(1) DEFAULT 'N' COMMENT '是否超集管理员',
`created_by` varchar(100) DEFAULT NULL COMMENT '创建人',
`created_at` datetime DEFAULT NULL COMMENT '创建人',
`updated_by` varchar(100) DEFAULT NULL COMMENT '更新人',
`updated_at` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `zm-user_id_uindex` (`id`),
UNIQUE KEY `zm-user_job_no_uindex` (`job_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表'
创建了一个包括 工号,姓名,性别,手机号 等属性的用户表,其中 id
是自增主键,job_no
唯一,且不允许为空。
接下来执行以下 sql
插入一条 张三 的数据:
insert into `zm-user` (id, job_no, name, sex, tel, email, address, status, is_super, created_by, created_at, updated_by,
updated_at)
values (1, '2023040100001', '张三', 'W', '13999999999', '13999999999@163.com',
'广东省佛山市顺德区某某镇某某小区某某栋某某号', 'Active', 'N', 'root', DATE('2024-04-04 00:00:01'), 'root',
DATE('2024-04-04 00:00:01'));
执行成功后,查询用户表可获得以下结果:
3.2 完善目录结构
如果一步步跟着操作过来的话,现在你的目录结构应该是这样的:
|-- server
|-- node_modules
|-- app.ts
|-- data-source.ts
|-- package.json
|-- pnpm-lock.yaml
|-- tsconfig.json
接下来我们调整目录结构如下:
|-- server
|-- node_modules
|-- src
|-- controllers
|-- user.controller.ts
|-- entities
|-- user.entity.ts
|-- services
|-- user.service.ts
|-- app.ts
|-- data-source.ts
|-- package.json
|-- pnpm-lock.yaml
|-- tsconfig.json
controllers
: 放置controller
层相关文件,即业务接口入口部分。entities
: 放置实体类文件。services
: 放置服务层文件。
3.2 新增实体类 entity
在 entities
目录下新建一个 user.entity.ts
,定义 User
实体的相关属性。
/**
* 用户实体
*/
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm'
@Entity('user')
export class User {
@PrimaryGeneratedColumn()
id!: number
@PrimaryColumn({ name: 'job_no' })
jobNo!: string
@Column()
name!: string
@Column()
sex!: string
@Column()
tel!: string
@Column()
email!: string
@Column()
address!: string
@Column({ name: 'created_by' })
createdBy!: string
@Column({ type: 'date', name: 'created_at' })
createdAt!: string
@Column({ name: 'updated_by' })
updatedBy!: string
@Column({ type: 'date', name: 'updated_at' })
updatedAt!: string
}
3.3 新增服务类 service
在 service
目录下新建一个 user.service.ts
,定义 User
相关处理逻辑,这里我们先创建一个 queryList
方法,用来查询并返回所有的用户数据及用户数。
/**
* 用户服务类
*/
import db from '../db'
import { User } from '../entities/user'
const userRepository = db.getRepository(User)
export class UserService {
// 查询全部用户
async queryList() {
return await userRepository.findAndCount()
}
}
3.4 新增 controller
在 service
目录下新建一个 user.controller.ts
,定义用户相关接口,这里我们先定义一个 queryList
的 get
接口。
/**
* 用户 controller
*/
import { Controller, Get } from 'routing-controllers'
import { UserService } from '../services/user.service'
@Controller('/user')
export class UserController {
userService
constructor() {
this.userService = new UserService()
}
@Get('/queryList')
queryList() {
return this.userService.queryList()
}
}
3.5 入口文件 app.ts
到这,我们已经完成了最基本的 用户管理模块,包含一个列表查询接口。
现在我们 pnpm start
启动服务,打开浏览器,输入 http://localhost:3000/user/queryList
来验证一下。
然而现实却给了当头一棒:
原来我们还没有处理入口文件,在 app.ts
中需要添加以下两步:
- 初始化
DataSource
. - 将
UserController
添加到createExpressServer
的controllers
参数中。
import { createExpressServer } from 'routing-controllers'
import { json, urlencoded } from 'body-parser'
import 'reflect-metadata'
import ds from './data-source'
import { UserController } from './src/controllers/user.controller'
// 新增:初始化 DataSource
ds.initialize()
.then(() => {
console.log('Data Source has been initialized!')
})
.catch((e: any) => {
console.log('Error during Data Source initialization:', e)
})
const app = createExpressServer({
// 新增:添加 UserController
controllers: [UserController]
})
// body 解析相关中间件
// 解析 json 格式
app.use(json())
// 解析 urlencoded body
// 会在 request 对象上挂载 body 属性,包含解析后的数据。
// 这个新的 body 对象包含 key-value 键值对,若设置 extended 为 true,则键值可以是任意累心个,否则只能是字符串或数组。
app.use(urlencoded({ extended: true }))
app.listen(3000, () => {
console.log(` App is running at http://localhost:3000\n`)
console.log(' Press CTRL-C to stop\n')
})
3.6 运行验证
关闭已有服务,重新执行 pnpm start
再次启动。
浏览器输入 http://localhost:3000/user/queryList
,我们来瞅瞅效果:
ok,接口数据已经返回,其中数组的第 1 个元素是 用户列表,第 2 个元素是数据总数,这是 typeorm
的 findAndCount
函数的返回结构。
规范化
到这,一个基础的 express
后端工程已经完成。但对于实际项目,除了实现业务功能外,还要考虑代码规范、团队协作、后期运维 等。
可以集成工具实现或配置文件(包括但不限于):
eslint
prettier
husky
commitlint
editorconfig
npmrc
nvmrc
由于规范化内容不是本文的重点,此处就不多占篇幅了,感兴趣的小伙伴可以前往《那些应该知道的前端规范工具》了解。
结语
好啦,以上就是搭建 express
工程的全部内容了,配套代码已上传至 GitHub
。本文从空目录开始,一步步集成 typescript
,typeorm
,routing-controllers
,mysql
等,直到最终实现业务模块并验证通过,旨在梳理后端工程的完整搭建过程。若您对于某个环节有疑问,欢迎评论区,一起沟通交流。
感谢阅读!愿你我共同进步,谢谢!!!
原文链接:https://juejin.cn/post/7349569626341081140 作者:是嘟老板呐