從0開始Typescript + Express + Sequelize + Swagger + PM2 + Docker 搭建部署後端服務
theme: smartblue
最新項目需要,就搭建了Node後端服務,記錄下整個流程,這裏不會太深入的説明每個插件的使用,僅對流程進行説明,具體可以查看對應工具的官網。
項目環境: - OS: m1、 - node: 17.9 - Dcoekr Image: node:18-alpine - 部署環境:centos
一、初始化項目
1、新建項目
express-demo
npm init
2、安裝必要的依賴
yarn add typescript ts-node @types/node @types/express cross-env nodemon -D
yarn add express
3、配置tsconfig.json, 常規配置
json
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs", //通過commonjs處理模塊化
"rootDir": "./",
"outDir": "./dist",
"esModuleInterop": true,
"baseUrl": "src",
"strict": true,
"strictPropertyInitialization": false // 不用嚴格要求值的初始化
},
"exclude": ["node_modules"]
}
4、在項目下新建src
目錄,用來存放源文件,項目目錄結構如下:
核心目錄就是:
- controllers
控制器,主要用來處理api邏輯
- models
模型,數據庫表模型
- services
操作數據庫的API
- databases
數據庫相關配置和初始化
然後在這個基礎上,我們還有一些輔助的目錄
- config
用來獲取外部傳進來的環境變量或者配置的數據庫參數
- exceptions
用來定義接口返回的JSON結構體
- interface
用來聲明變量類型
- routes
用來暴露對外的API接口
- utils
作為工具函數的文件目錄
- app.ts
用來構建整個app,將各種需要提前處理的集中處理
- index.ts
用來作為整個項目的入口文件
當前節點用到的插件
入口文件index.ts
, 我們用來引入路由和啟動服務
```ts import App from './app' import monitorRouter from './routes/monitor.route'
const app = new App([new monitorRouter()]) app.listen() ```
app.ts
,當app實例化的時候,要連接數據庫、初始化路由、中間件、文檔等,這裏先定義好方法
``ts
class App {
app: express.Application
port: number = 3000
constructor(routers: Routes[]){
this.app = express();
this.connectToDatabase()
this.initializeMiddlewares()
this.initializeRoutes(routers)
this.initializeSwagger()
}
// 連接數據庫
private connectToDatabase() {
DB.sequelize.sync({ force: false });
}
// 初始化中間件
private initializeMiddlewares() {
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
}
// 初始化路由
private initializeRoutes(routes: Routes[]) {
routes.forEach(route => {
this.app.use('/', route.router);
});
}
// 初始化接口文檔
private initializeSwagger() {
// 生成文檔路由
this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
}
// 啟動服務
public listen(){
this.app.listen(this.port, () => {
console.log(
TypeScript with Express
http://localhost:${this.port}/`);
});
}
}
export default App ```
二、初始化路由
路由類,這樣就在index.ts中引入路由實例化,所有的路由就生效了,然後我們看每個路由對應的都是controller的方法,只要實現這些方法就可以了
```ts import { Router } from 'express'; import MonitorsController from '../controllers/monitor.controller'; import { Routes } from '../interfaces/routes.interface';
class MonitorRoute implements Routes { public path = '/v1/monitor'; public router = Router(); public monitorsController = new MonitorsController();
constructor() { this.initializeRoutes(); }
private initializeRoutes() {
this.router.get(${this.path}
, this.monitorsController.getMonitor);
this.router.post(${this.path}
, this.monitorsController.createMonitor);
this.router.post(${this.path}/:id
, this.monitorsController.getMonitorById);
}
}
export default MonitorRoute; ```
三、初始化數據庫
yarn add sequelize mysql2
安裝兩個依賴,一個操作數據庫的orm,一個是數據庫驅動。
在databases
中,我們初始化數據庫
new Sequelize.Sequelize(database, user, password, {
dialect: 'mysql',
host: DB_HOST,
port: DB_PORT
} as any);
四、配置區分生產環境和預發環境
在package.json
下新增scripts
命令,配置項目以不同的環境變量啟動
"start": "cross-env NODE_ENV=development nodemon src/index.ts",
"start:prod": "cross-env NODE_ENV=pruduction nodemon src/index.ts",
然後在項目根目錄下新建.env.development.local
文件,配置變量
```.env PORT = 3000
DB_HOST = localhost
DB_PORT = 3306
DB_USER = root
DB_PASSWORD = 12345678
DB_DATABASE = stark
安裝`dotenv`可以讀取各種環境變量
yarn add dotenv
在`config/index.ts`中,讀取環境變量,在其他地方共享
import { config } from 'dotenv';
config({ path: .env.${process.env.NODE_ENV || 'development'}.local
});
export { PORT } = process.env
```
之後根據業務需要寫controller和service就可以了
五、引入swagger
如圖所示,可以自動生成API,這樣就不用,每次單獨寫了
安裝依賴
```
yarn add swagger-jsdoc swagger-ui-express
yarn add @types/swagger-jsdoc @types/swagger-ui-express -D
配置swagger文檔,讀取對應的yaml文件,生成對應的路由,然後在項目初始化的時候執行該函數
ts
private initializeSwagger() {
const options = {
swaggerDefinition: {
info: {
title: 'REST API',
version: '1.0.0',
description: 'Example docs',
},
},
apis: ['swagger*.yaml'],
};
const specs = swaggerJSDoc(options);
this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
} ``` yaml配置文件:配置文檔路由和Modal(可以直接通過註釋生成,需要自行查看文檔)
```yaml tags: - name: monitors description: monitors
paths:
[GET] monitors
/v1/monitor: get: tags: - monitors summary: Find All monitors responses: 200: description: "OK" 500: description: "Server Error"
definitions
definitions: monitors: type: object required: - msg properties: msg: type: string description: error message column: type: number description: error column ```
六、配置PM2啟動服務
在部署項目之前,需要先編譯成js,這裏我們使用swc(通過 rust 實現的 babel:swc,一個將 ES6 轉化為 ES5 的工具)當然也可以配置webpack啥的。
yarn add @swc/cli @swc/core -D
在根目錄下新建文件配置文件.swcrc
,這裏附一份配置,具體的內容可以查看文檔
js
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": false,
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
},
"target": "es2017",
"externalHelpers": false,
"keepClassNames": true,
"loose": false,
"minify": {
"compress": false,
"mangle": false
},
"baseUrl": "src",
"paths": {
"@/*": ["*"]
}
},
"module": {
"type": "commonjs"
}
}
在package.json
中配置scripts
命令
"build": "swc src -d dist --source-maps --copy-files",
執行yarn build,就可以看到根目錄下生成了dist目錄,就是解析後的js文件,要部署的也是這個文件
接着我們使用pm2啟動項目
yarn add global pm2
因為pm2運行時肯定要區分生產環境和預發環境,所以我們需要給pm2增加配置文件.ecosystem.config.js
js
module.exports = {
apps: [
{
name: 'monitor', // pm2 start App name
script: 'dist/index.js',
autorestart: true, // auto restart if process crash
watch: false, // files change automatic restart
ignore_watch: ['node_modules', 'logs'], // ignore files change
max_memory_restart: '1G', // restart if process use more than 1G memory
merge_logs: true, // if true, stdout and stderr will be merged and sent to pm2 log
output: './logs/access.log', // pm2 log file
error: './logs/error.log', // pm2 error log file
env_test: {
PORT: 3000,
NODE_ENV: 'development',
DB_HOST: "localhost",
DB_PORT: 3306,
DB_USER: "root",
DB_PASSWORD: 12345678,
DB_DATABASE: "stark"
},
env_production: { // environment variable
PORT: 3000,
NODE_ENV: 'production',
DB_HOST: "localhost",
DB_PORT: 3306,
DB_USER: "root",
DB_PASSWORD: "12345",
DB_DATABASE: "monitor"
}
}
]
};
執行命令pm2 start ecosystem.config.js --env test
到這裏已經可以部署項目成功了,接着我們通過Docker部署一下
七、Docker構建鏡像並部署
```Dockerfile FROM node:18-alpine as common-build-stage
COPY . ./app
WORKDIR /app
RUN npm i -g pm2 --registry=http://registry.npm.taobao.org && yarn add production
EXPOSE 3000
FROM common-build-stage as production-build-stage
ENV NODE_ENV production
CMD ["pm2-runtime", "start", "ecosystem.config.js", "--env", "production"]
``
這裏使用
pm2-runtime,是因為如果pm2的話,Docker監聽不到服務的運行,就會退出,所以這裏pm2官方給出了
pm2-runtime`來解決這個問題
docker build -t demo --platform linux/amd64 --target production-build-stage -f Dockerfile .
我們要構建的鏡像最終是要部署到centos上的,但是m1下打包的無法兼容,所以增加參數--platform linux/amd64
就可以了
八、項目中使用的插件
- 從0開始Typescript Express Sequelize Swagger PM2 Docker 搭建部署後端服務
- 如何從0設計開發一款JS-SDK
- 聊聊中後台產研一體化:引子
- 面向中後台複雜場景的低代碼實踐思路
- 聊聊中後台前端應用:上下文的那些事兒
- 聊聊中後台前端應用:業務中的組件體系
- 聊聊中後台前端應用:模塊相關的一些事
- tomcat配置虛擬路徑,可以解決實際開發中測試時前端訪問後台電腦上的圖片的問題
- 在微信框架模塊中,基於Vue&Element前端的後台管理功能介紹
- 聊聊中後台前端應用:前言
- 【以模塊化的思想開發中後台項目】第一章
- 【建議追更】以模塊化的思想來搭建中後台項目
- 前端傳給後端的xml字符串被後台過濾掉,數據庫中只保存值的問題
- 中後台前端搭建經驗總結-技術細節篇(一)
- 中後台前端搭建經驗總結-技術細節篇(一)
- 中後台前端的 Serverless 還能這麼玩
- 如何藉助社區能力快速構建中後台生態,飛豬中後台的探索和實踐