使用 Angular、NestJS 和 Nx 构建全栈 Web 应用程序 - 天作之合 为什么要写这篇文章?搭建脚手架 运行项目 调整应用程序以进行开发 从单个服务器提供前端和后端以进行生产 打包应用程序以进行部署

2025-06-07

使用 Angular、NestJS 和 Nx 构建全栈 Web 应用程序——天作之合

为什么要发这个帖子?

脚手架

运行项目

调整应用程序以进行开发

通过单个服务器为生产环境提供前端和后端服务

打包应用程序以进行部署

为什么要发这个帖子?

过去几年,Angular 一直是我首选的前端框架,我非常喜欢它。然而,如果你想创建一个全栈 Web 应用,它只能满足一半的需求。一段时间以来,我一直将它与 NestJS 搭配使用,并且在多个项目中运行良好。

我一直向朋友和同事推荐这个组合,所以我终于想把我用的这个组合写下来,作为参考/入门。也许这篇文章能激励你也尝试一下。如果是这样,请告诉我你的效果如何。

简而言之:在本例中, Angular将作为我们的前端框架, NestJS作为我们的后端,所有这些都将完美地集成在Nx工作区中。我们将引入一些生活质量改进,并将两个项目捆绑到一个包中。

简单回顾一下,NestJS 是一个基于 Express 或 Fastlify 的抽象层,这两个框架都是基于 NodeJS 的 REST 框架。它用 Typescript 编写,并遵循 Angular 中的许多相同模式(注解、依赖注入等)。Nx 是由前 Google 员工开发的一套工具,用于帮助 Monorepos 开发。它们还提供了非常合理的默认选项和配置覆盖。

准备好了吗?出发!

脚手架

首先,我们将设置一个新的 Nx 工作区并在其中创建一个 Angular 和 NestJS 应用程序:

⇒  npx create-nx-workspace@latest

? Workspace name (e.g., org name)     my-fullstack-project
? What to create in the new workspace angular-nest      
? Application name                    client
? Default stylesheet format           CSS
? Use the free tier of the distributed cache provided by Nx Cloud? No  
// (to keep things simple for now)

完成后,将执行以下操作:

  1. 创建一个名为“工作区”的新目录
  2. 生成一个新的 Angular 项目,位于apps/client
  3. 创建新的 NestJS 项目,位于apps/api
  4. 在工作区内生成一个示例 TypeScript 库 - 供客户端和 API 使用(位于 下libs/api-interfaces

同样巧妙的是,Nx 为这两个项目提供了改进的工具,例如:

  • 使用 Jest 而不是 Karma 作为单元测试运行器
  • 使用 Cypress 而不是 Protractor 来满足 E2E 规范(仅限 Angular)
  • 使用 Prettier 自动格式化代码
  • 适合整个工作区的 TSLint 设置

Nx还提供了大量其他功能,但这些是我现在想要指出的。

运行项目

现在我们已经设置好了基本项目,是时候启动它们了。打开两个终端窗口并运行以下命令:

[Terminal 1]: npm run nx -- serve client
[Terminal 2]: npm run nx -- serve api

第一个命令将运行默认的 Angular 开发服务器,编译前端并将其服务于http://localhost:4200下的服务。第二个命令将启动 NestJS 开发服务器,将 Typescript 编译为与 NodeJS 兼容的 JavaScript,并将结果服务于http://localhost:3333/api下的服务。此外,Nx 还为 Angular 开发服务器添加了代理配置,它会将对http://localhost:4200/api 的请求代理到 NestJS 开发服务器,这样我们在开发过程中就不会遇到任何 CORS 问题。代理配置可以在下方找到apps/client/proxy.conf.json,并在项目定义文件中引用angular.json

如果您现在打开浏览器访问http://localhost:4200,您应该会看到类似这样的页面:

初始 Angular/Nx 欢迎页面

如果您检查网络并刷新,您将看到对http://localhost:4200/api/hello 的XHR 请求,表明代理已正确设置。

等等,什么?

让我解释一下发生了什么:

NestJS 项目配置了一个前缀,用于所有控制器。该前缀在 ( apps/api/src/main.ts)下定义如下:

  const globalPrefix = 'api';
  app.setGlobalPrefix(globalPrefix);

Nx 还生成了一个示例 REST 控制器,称为 AppController(apps/api/src/app/app.controller.ts),它目前提供静态数据块,在我们的例子中,它是 Typescript 接口的一个对象,Message定义如下(libs/api-interfaces/src/lib/api-interfaces.ts):

export interface Message {
  message: string;
}

由于该接口Message定义在共享库中,因此前端和后端都可以使用它。太棒了,两个项目都实现了类型安全!

AppController 方法带有注释

  @Get('hello')
  getData(): Message {
    return this.appService.getData();
  }

它告诉 NestJS在服务器运行时在http://localhost:3333/api/hello下公开一个新的 GET 端点并返回我们的消息对象。

在堆栈的另一端,我们有 Angular 项目,它被配置为向这个端点发出一个示例 GET 请求。示例代码可以在前端的 AppComponent 中找到,位于apps/client/src/app/app.component.ts

export class AppComponent {
  hello$ = this.http.get<Message>('/api/hello');
  constructor(private http: HttpClient) {}
}

并在模板中直接使用异步管道调用它:

<div>Message: {{ hello$ | async | json }}</div>

现在,由于 Angular 开发服务器运行在http://localhost:4200上,由于调用时使用了相对路径,请求将转到http://localhost/4200/api/hello。AngularhttpClient代理配置会捕获该请求,并将其转发到我们的 NestJS 服务器。太棒了🎉

调整应用程序以进行开发

恭喜!现在我们有一个同时运行 NestJS 和 Angular 的工作区,以及关于如何从前端调用后端的示例代码。这已经相当不错了!

为了获得更好的开发体验,我喜欢用一个命令并行启动前端和后端 dev-server。为此,我们将安装一个名为“concurrently”的 node-helper 。

npm install --save-dev concurrently

一旦完成,我们可以进行package.json如下调整:

"start:fe": "ng serve client",
"start:be": "ng serve api",
"dev": "concurrently -p=\"{name}\" -n=\"Angular,NestJS\" -c=\"green,blue\" \"npm run start:fe\" \"npm run start:be\"",

现在运行npm run dev会并行启动两个开发服务器,并在终端的每一行前面加上“Angular”或“NestJS”作为前缀,并将前缀分别标记为绿色和蓝色。太棒了!

同时运行 Angular 和 NestJS 开发服务器的输出

通过单个服务器为生产环境提供前端和后端服务

好了,现在我们已经有了一个可以开发的全栈应用程序。接下来,让我们将应用程序打包成一个可执行的 npm 包,用于前端和后端的开发。

为了实现这一点,我们将创建 Angular 应用程序的生产版本,并配置 NestJS 服务器以在其根路径下为前端提供服务,以便我们可以将两者捆绑在一个独立的包中(例如将其部署到 CloudFoundry 或其他云提供商)。

首先,我们将创建 Angular 前端的生产版本:

⇒  npm run nx -- build client --prod

> my-fullstack-project@0.0.0 nx /Users/hrichert/Projects/my-fullstack-project
> nx "build" "client" "--prod"


> ng run client:build:production
Generating ES5 bundles for differential loading...
ES5 bundle generation complete.

// ... A few seconds later...

Date: 2020-06-17T16:14:41.427Z - Hash: 4957569a994e1b83d273 - Time: 34832ms

现在,我们的前端编译(最小化、丑化、填充)输出可以在以下位置找到dist/apps/client

让我们配置 NestJS 以便在访问其根路径时为该文件夹提供服务。

首先,我们将安装 NestJSserve-static包以提供静态资产服务

npm install --save @nestjs/serve-static

现在我们需要做的就是ServeStaticModule在 AppModule 中导入并配置此包提供的内容(apps/api/src/app/app.module.ts

import { ServeStaticModule } from '@nestjs/serve-static';
import { join } from 'path';

// ...

@Module({
  imports: [
    ServeStaticModule.forRoot({
      rootPath: join(__dirname, '..', 'client'),
    }),
  ],
  // ...
})
export class AppModule {}

../client它指示 NestJS从其自己的 dist 文件夹( )遍历dist/apps/api并在其根路径上提供内容。

果然,仅通过运行后端npm run start:be并导航到http://localhost:3333/应该会产生与之前运行 Angular dev-server 时相同的结果:

初始 Angular/Nx 欢迎页面

打包应用程序以进行部署

现在剩下的就是把所有东西打包到一个 npm 包里,然后就可以部署我们的全栈应用程序了。现在就开始吧。

我们需要编辑 package.json 以仅包含 dist 文件,并且为了方便起见,我们将添加一个启动脚本:

{
    ...
    "files": ["dist/apps/client", "dist/apps/api"],
    ...
    scripts: {
    "serve": "node dist/apps/api/main.js",
    ...
    }
}

现在,如果我们运行npm pack,将在项目目录中为我们生成一个 tarball 文件:

⇒  npm pack
npm notice
npm notice 📦  my-fullstack-project@0.0.0
npm notice === Tarball Contents ===
// ...
npm notice === Tarball Details ===
npm notice name:          my-fullstack-project
npm notice version:       0.0.0
npm notice filename:      my-fullstack-project-0.0.0.tgz
npm notice total files:   14

npm install --production我们现在可以将这个包上传到我们最喜欢的云提供商,在上传后运行它,并将serve脚本作为包的启动命令提供(此步骤因您的云提供商而异)。

当然,您也可以在本地运行此包,只需解压 tarball 文件,npm install --production在目录内运行,然后运行npm run serve

希望这篇文章对你有所帮助。如果你遗漏了任何重要步骤,请告诉我!现在我们有了这些设置,显然还有很多事情可以做,比如前端或后端的运行时类型验证,通过在同一个工作区中使用共享库在前端和后端之间共享逻辑等等。如果你对这些主题感兴趣,我很乐意为这篇文章写一篇后续文章 :)

感谢阅读!

文章来源:https://dev.to/hendrikfoo/building-full-stack-web-applications-with-angular-nestjs-and-nx-a-match-made-in-heaven-5fh7
PREV
6 个灵光乍现的时刻
NEXT
介绍递归管道和组合类型简介创建递归管道类型实现管道函数组合关闭