最新项目需要,就搭建了Node后端服务,记录下整个流程,这里不会太深入的说明每个插件的使用,仅对流程进行说明,具体可以查看对应工具的官网。
附项目源码:github.com/richLpf/exp…
项目环境:
- 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, 常规配置
{
"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操作数据库的APIdatabases数据库相关配置和初始化
然后在这个基础上,我们还有一些辅助的目录
config用来获取外部传进来的环境变量或者配置的数据库参数exceptions用来定义接口返回的JSON结构体interface用来声明变量类型routes用来暴露对外的API接口utils作为工具函数的文件目录app.ts用来构建整个app,将各种需要提前处理的集中处理index.ts用来作为整个项目的入口文件
当前节点用到的插件
入口文件index.ts, 我们用来引入路由和启动服务
import App from './app'
import monitorRouter from './routes/monitor.route'
const app = new App([new monitorRouter()])
app.listen()
app.ts,当app实例化的时候,要连接数据库、初始化路由、中间件、文档等,这里先定义好方法
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的方法,只要实现这些方法就可以了
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文件,配置变量
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文件,生成对应的路由,然后在项目初始化的时候执行该函数
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(可以直接通过注释生成,需要自行查看文档)
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,这里附一份配置,具体的内容可以查看文档
{
"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
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构建镜像并部署
FROM node:18-alpine as common-build-stage
COPY . ./app
WORKDIR /app
RUN npm i -g pm2 --registry=https://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 就可以了