发布时间:2025年4月11日

NestJS 是一个用于构建高效且可伸缩的服务端应用的框架,有以下几个特点:
因此,相较于市面上其他的 Node.js 框架,Nest.js 应用的架构设计充斥着严谨、理性的美,使其非常适合构建企业级应用 。

| 概念 | 作用 | 主要用途 |
|---|---|---|
| Module | 组织代码,定义应用的结构 | 将相关功能分组 (Controller、Provider) |
| Provider | 提供可注入的服务(如业务逻辑、数据库操作) | 封装可复用的逻辑 (Service、Repository、Helper) |
| Controller | 处理客户端请求并返回响应,通过组合不同的 Service 形成业务逻辑 | 定义路由(REST API 端点) |
| Service | 实现业务逻辑 | 处理复杂逻辑(如数据库查询、计算) |
| Pipe | 转换或验证输入数据 | 参数校验(如 DTO)、数据转换(如字符串转数字) |
| Guard | 决定请求是否允许访问(权限控制) | 身份验证(JWT)、角色检查(RBAC) |
| Exception Filter | 捕获并处理未捕获的异常 | 统一错误响应格式、日志记录 |
| Middleware | 在请求到达 Controller 前或响应返回前执行代码 | 日志记录、请求修改(如添加 Headers)、跨域处理(CORS) |
| Interceptor | 在请求/响应流程中插入逻辑(AOP 风格) | 日志记录、性能监控、响应数据转换 |
在 NestJS 项目中,有两个概念充斥着整个项目:依赖注入( Dependency Injection, DI )、控制反转( Inversion of Control, IOC )
控制反转是一种设计原则,其中对象的行为控制权被反转或转移到对象外部 。程序的流程由框架或外部服务决定,而不是直接由程序员控制 。与传统编程中自定义代码调用可重用库来处理通用任务不同,使用控制反转时,外部代码或框架处于控制地位并调用自定义代码
控制反转的核心思想是:
将任务的执行与实现解耦,比如:我需要一把锤子,但我不想知道锤子是怎么造(构建)的
控制反转的思想其实很常见,比如:
connect:组件不关心 state、dispatch 是怎么注入的依赖注入不是一种新的设计原则,它是一种实现控制反转的方式,因此他们的核心思想都是一样的,
比如我们有一个锤子类
class Hammer {
hit() {
console.log("敲钉子!");
}
}
在过往的设计中我们使用时,可能是这样的
class Person {
private hammer: Hammer;
constructor() {
this.hammer = new Hammer(); // 自己创建依赖
}
}
但在 NestJS 中我们是这样使用的
// 声明一个可注入的锤子类
class Hammer {
hit() {
console.log("敲钉子!");
}
}
class Person {
constructor(private hammer: Hammer) {} // NestJS 自动注入 Hammer 实例
useHammer() {
this.hammer.hit(); // 直接使用注入的依赖
}
}
一个功能完备的服务端应用通常会依赖许多外部资源,比如:Redis、数据库、网关等。为了保证应用在开发和生产环境中的稳定性和一致性,我们需要对这些依赖进行有效的管理和配置。常见的做法是通过配置文件统一管理这些外部依赖的连接信息,例如:资源地址、端口、用户名和密码等。同时,不同环境可能有不同的配置需求,因此我们通常会引入环境变量或配置管理工具,以便灵活适配各种场景。
为了简化外部依赖的部署和管理过程,Docker Compose 是一个非常实用的工具。通过 Docker Compose,可以将应用及其所需的外部依赖统一定义在一个配置文件中,并通过简单的命令实现一键启动。这种方式不仅能确保开发和生产环境的一致性,还能大幅降低环境配置的复杂度。在开发环境中,开发者可以快速拉起所有依赖服务,在生产环境中也能通过相同的配置文件实现高效部署。
下面就是我们项目在开发和生产环境中的 docker-compose.yaml
services:
postgres:
image: postgres:16-alpine
container_name: orion-postgres
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orion
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
container_name: orion-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
command: redis-server --appendonly yes
volumes:
postgres_data:
redis_data:
services:
orion:
image: ghcr.io/mocha-fe/orion:latest
ports:
- "80:3000"
volumes:
- /etc/localtime:/etc/localtime:ro
networks:
- app-network
redis:
image: redis:7-alpine
container_name: orion-redis
restart: always
expose:
- 6379
volumes:
- redis_data:/data
command: redis-server --protected-mode no
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
networks:
- app-network
postgres:
image: postgres:16-alpine
container_name: orion-postgres
restart: always
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: orion
expose:
- 5432
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
redis_data:
networks:
app-network:
driver: bridge
可以看到,生产环境和开发环境的 docker-compose.yaml 文件在结构和内容上基本是相同的,这种一致性使得我们能够保证整套系统的外部依赖相同,也能保证在部署到生产时尽可能的减少因为环境差异导致的部署失败问题。
当然,生产环境在配置细节上更注重安全性和稳定性,比如使用 expose 暴露 redis 和 postgresql 容器,而不是使用 ports,从而保证我们的 redis 与 postgresql 与外部网络的隔离
GitHub Actions 是一个持续集成和持续交付 (CI/CD) 平台,我们可以使用它来实现自动化构建、测试和部署工作。
在我们的项目的 .github/workflows 下你可以创建多个工作流配置文件,当代码推送到远程时,github 会自动执行它们,下面是目前在用的容器化工作流
name: Docker Image CI
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to GitHub Container Registry
run: echo "${{ secrets.PACKAGE_WRITE }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
- name: Build and push Docker image
id: docker_build
run: |
IMAGE_NAME=ghcr.io/mocha-fe/orion:latest
docker build . --tag $IMAGE_NAME
docker push $IMAGE_NAME

最后通过 ssh 命令进入到服务器更新 docker 镜像并重启即可