使用 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)
完成后,将执行以下操作:
- 创建一个名为“工作区”的新目录
- 生成一个新的 Angular 项目,位于
apps/client
- 创建新的 NestJS 项目,位于
apps/api
- 在工作区内生成一个示例 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,您应该会看到类似这样的页面:
如果您检查网络并刷新,您将看到对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”作为前缀,并将前缀分别标记为绿色和蓝色。太棒了!
通过单个服务器为生产环境提供前端和后端服务
好了,现在我们已经有了一个可以开发的全栈应用程序。接下来,让我们将应用程序打包成一个可执行的 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 时相同的结果:
打包应用程序以进行部署
现在剩下的就是把所有东西打包到一个 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