express 是node
生态中非常优秀的框架,大部分的业务接口,我们都可以通过它来实现。
有时候我们想使用 typescript开发业务,然后使用 typeorm
链接我们的 mysql
数据库, 应该怎么创建我们的项目呢?
在使用 typeorm
的时候, 可能很多人看到这个 ORM
大部分使用的 装饰器
, 今天我们用 express
去集成一下
纯的用 typeorm 可能你没有啥问题, 但是 typescript typeorm 集成可能会出现各种各样的 bug
, 今天手把手我们一起实践
注:纯 js版本写 typeorm 的 实体也是可以的, 不一定要写 装饰器的class 定义实体
我们现在开始吧!
环境安装
代码语言:shell复制pnpm init
pnpm add express -S
pnpm add @types/express typescript @types/node -D
# 安装 typeorm
pnpm add typeorm reflect-metadata -S
# 数据库
pnpm add mysql2 -S
# 安装 `ts-node` 支持在 开发环境运行 ts 代码
# 安装 `nodemon` watch文件的变动
pnpm add ts-node nodemon -D
包版本
编写本文,所有包均为最新
代码语言:json复制{
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --exec 'ts-node' src/app.ts",
"local:prod": "cross-env NODE_ENV=production node dist/app.js",
"build": "tsc"
},
"dependencies": {
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"mysql2": "^3.11.0",
"reflect-metadata": "^0.2.2",
"typeorm": "^0.3.20"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/node": "^22.2.0",
"cross-env": "^7.0.3",
"nodemon": "^3.1.4",
"prettier": "^3.3.3",
"ts-node": "^10.9.2",
"typescript": "^5.5.4"
}
}
typescript配置
代码语言:shell复制# 初始化一个配置文件
npx tsc --init
将装饰器相关的配置启用
- experimentalDecorators, emitDecoratorMetadata
- strictPropertyInitialization 设置为false, 避免在写实体类的时候,没有给属性初始化,然后出现警告
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowJs": true,
"strictPropertyInitialization": false,
"rootDir": "./src",
"baseUrl": "./",
"outDir": "./dist",
"esModuleInterop": true
}
}
不要动 target
, module
, moduleResolution
配置, 就默认注释掉就行;
否则 在dev 阶段,会出现各种 ts-node无法解析ts文件的问题
项目目录
创建基础的项目目录, app.ts 为程序主入口
代码语言:shell复制- src
- config 配置文件
- db.ts
- controller 路由文件
- index.ts
- user.ts
- permission.ts
- ....
- db 数据库相关
- datasource.ts 数据库初始化
- entity 数据库实体
- Factory.ts 测试使用的
- service 逻辑处理
- app.ts 程序主入口
- package.json
- nodemon.json nodemon.json配置文件
- .gitignore
- .env
- .env.development
- .env.production
数据库初始化
src/db/datasource.ts
代码语言:ts复制import { DataSource } from 'typeorm'
import config from '../config/db'
export const dataSource = new DataSource(config)
src/config/db.ts
代码语言:ts复制import { type DataSourceOptions } from 'typeorm'
import { Factory } from '../entity/Factory'
const config: DataSourceOptions = {
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: '123456',
database: 'blog',
entities: [Factory],
// 是否自动同步
synchronize: true
}
export default config
定义实体类
src/entity/Factory.ts
代码语言:ts复制import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'
@Entity()
export class Factory {
@PrimaryGeneratedColumn('increment', {
comment: '工厂id'
})
id: number
@Column({
comment: '工厂名称',
type: 'varchar',
length: 150
})
name: string
@Column({
type: 'varchar',
length: 255,
comment: '工厂地址'
})
address: string
}
环境变量
在不同的环境,我们需要加载不同的配置文件,使用 env 进行管理
代码语言:shell复制pnpm add dotenv -S
pnpm add cross-env -D
.env 编写通用的内容
.env.development 开发环境
代码语言:shell复制PORT=3000
.env.production 生产环境
代码语言:shell复制PORT=4000
主程序
- PORT 是
env
里面拿到的
import 'reflect-metadata'
import express from 'express'
// 加载数据库
import { dataSource } from './db/datasource'
import router from './controller'
import dotenv from 'dotenv'
console.log('当前环境', process.env.NODE_ENV)
dotenv.config({
path: ['.env', '.env.' process.env.NODE_ENV]
})
const app = express()
// 数据库初始化
dataSource
.initialize()
.then(() => {
// 需要再数据库初始化完成后才去初始化server 服务,避免在server服务中有一些 定时任务,或者其他的直接就调用 数据库操作,导致偶发报错
// 因为异步问题,偶发报错就不好排查
app.use('/api', router)
app.listen(process.env.PORT, () => {
console.log('服务启动, http://localhost:' process.env.PORT)
})
})
.catch((e) => {
console.log('数据库启动失败')
console.log(e)
})
- 接口的
api
前缀是在这里指定的。
路由拆分
我们在 src/controller/index.ts
中编写路由
/**
* 统一维护路由
*/
import express from 'express'
const router = express.Router()
import user from './user'
import permission from './permission'
router.use('/user', user)
router.use('/permission', permission)
export default router
src/controller/permission.ts
import express from 'express'
const router = express.Router()
// http://localhost:3000/api/permission/getMenu
router.get('/getMenu', (req, res) => {
res.json({
code: 0,
data: null,
message: 'success'
})
})
// http://localhost:3000/api/permission/menu
router.post('/menu', (req, res) => {
res.json({
code: 0,
data: null,
message: 'success'
})
})
export default router
其他路由文件,照葫芦画瓢即可。
现在路由都规划好了,需要集成到 应用中
src/app.ts
代码语言:ts复制import router from './controller'
const app = express()
... 省略了其他代码
app.use('/api', router)
监听文件变动
开发阶段,我们需要监听文件变动,自动重启服务; 使用 nodemon
比较轻松的做到这一点
在根目录创建配置文件
nodemon.json
代码语言:json复制{
"watch": [
"src"
],
"ext": "js,ts"
}
启动脚本
上面所有流程都做了以后,我们已经从项目的 初始化
,依赖安装
, 路由规划
,环境变量加载
等等,完成了一个项目的 90%, 现在需要编写我们的 启动脚本
package.json
{
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon --exec 'ts-node' src/app.ts",
"local:prod": "cross-env NODE_ENV=production node dist/app.js",
"build": "tsc"
},
}
- 脚步执行过程中,我们需要注入 NODE_ENV ,以区分不同的环境
- 使用 nodemon 监听我们的文件变动,从而重启服务 然后将 ts-node 作为子进程执行
- ts-node 会自动读取到 项目的根目录的 tsconfig.json 配置文件,然后执行主入口 src/app.ts 程序
- 在生产环境, 我们应该先执行
build
将typescript 编译为 js文件,然后再执行local:pord
脚步,启动项目 package.json
中的type:'module'
字段不要加
编写接口
现在,我们简单的编写一个接口,测试数据的写入
src/controller/user.ts
代码语言:ts复制import express from 'express'
import { dataSource } from '../db/datasource'
import { Factory } from '../entity/Factory'
const factory = dataSource.getRepository(Factory)
const router = express.Router()
router.get('/', async (req, res) => {
// 测试代码
await factory.save({
name: '测试工厂',
address: '测试地址'
})
res.json({
code: 0,
data: null,
message: 'success'
})
})
export default router
访问地址: http://localhost:3000/api/user
数据写入成功
小结
至此, 你已经掌握了一个简单的后台项目如何搭建,项目结构如何组织,路由管理等基础知识。
大型项目会有更多的内容,比如 日志管理, CORS, 鉴权等等, 这些都是在一点一点的叠加的,只要掌握了基础的内容,我们就能自行 添砖加瓦
项目模板地址: https://gitee.com/luoriwusheng/express-ts-typeorm-template.git
如果你有任何问题,欢迎留言,我们一起探讨~