使用【Nx+Angular+NestJS+Firebase】,在15分钟内构建并部署完整的PWA环境
大意
在重新审视构建全栈PWA环境时,我遇到了@nrwl/nx,并尝试使用它。
我能在大约15分钟的时间内完成从环境设置到部署的步骤,所以我来分享一下我的工作流程。如果能写好草图的话,可能会更快一些。

要达到目标,我们必须首先满足这个前提。
- firebase-toolsの初期設定が完了している
工作流程
-
- ① Nx Workspaceの作成(3分くらい)
-
- ② @angular/pwaのインストール(30秒くらい)
-
- ③ Firebaseプロジェクトの作成(30秒くらい)
-
- ④ @angular/fireのインストール(2分)
-
- ⑤ NestJSをCloud Functions for Firebaseに対応(4分くらい)
-
- ⑥ Firebaseのプラン変更(30秒くらい)
- ⑦ デプロイ(4分くらい)
创建Nx Workspace
首先,使用”create-nx-workspace”命令创建Nx的工作区。
基本上,使用npx命令进行操作。
$ npx create-nx-workspace@latest pwa-sample \
--preset angular-nest --app-name ng-pwa-sample \
--style scss --linter eslint --nx-cloud false
当PWA示例的创建工作完成后,移动到已完成的pwa-sample目录中。
$ cd pwa-sample
② 安装@angular/pwa
使用nx add来运行Schematics,就像使用ng add一样,以安装@angular/pwa以实现PWA支持。
$ npx nx add @angular/pwa
创建Firebase项目
在安装 @angular/fire 之前,请先创建一个 Firebase 项目。
$ PROJECT_NAME=sample-pwa
$ PROJECT_ID=$PROJECT_NAME-$(printf %04d $(($RANDOM % 10000)))
// sample-pwa-0021
$ npx firebase projects:create $PROJECT_ID --display-name $PROJECT_NAME
安装 @angular/fire
创建Firebase项目后,安装@angular/fire。
$ npx nx add @angular/fire
在安装时会询问要使用的Firebase项目,所以请选择之前创建的项目。
将NestJS适配到Cloud Functions for Firebase。
设置.firebaserc文件配置
在部署Cloud Functions时需要的一项是将默认项目设置为.firebaserc,可以使用以下命令进行设置。
$ npx firebase use $PROJECT_ID --alias default
安装必需的软件包
同时,安装Firebase所需的Cloud Functions包。
$ npm install firebase-functions firebase-admin express
将Cloud Functions中调用NestJS的配置。
在apps/api/src中创建main.prod.ts文件,并写入以下内容。
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as express from 'express';
import * as functions from 'firebase-functions';
import { AppModule } from './app/app.module';
export async function bootstrap(expressInstance) {
const app = await NestFactory.create(
AppModule,
new ExpressAdapter(expressInstance)
);
const globalPrefix = 'api';
app.setGlobalPrefix(globalPrefix);
await app.init();
}
const expressServer = express();
export const api = functions.region('us-central1')
.https.onRequest(async (request, response) => {
await bootstrap(expressServer);
expressServer(request, response);
});
部署的配置1(编辑angular.json)
在 angular.json 中进行如下编辑:
第一项编辑是在生产环境中使用 main.prod.ts 替代 main.ts 的配置,第二项是配置 NestJS api 的部署命令。
{
"version": 1,
"projects": {
...,
"api": {
...,
"architect": {
"build": {
"configurations": {
"production": {
...,
"fileReplacements": [
{
"replace": "apps/api/src/environments/environment.ts",
"with": "apps/api/src/environments/environment.prod.ts"
},
+ {
+ "replace": "apps/api/src/main.ts",
+ "with": "apps/api/src/main.prod.ts"
+ }
]
}
}
},
+ "deploy": {
+ "builder": "@nrwl/workspace:run-commands",
+ "options": {
+ "commands": [
+ {
+ "command": "npx firebase deploy --only functions"
+ }
+ ]
+ }
+ }
}
}
}
}
部署设置②(准备package.json的转换脚本)
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import * as fs from 'fs';
async function main() {
const BASE_PATH = join(__dirname, '..', '..');
console.debug('BASE_PATH:', BASE_PATH);
const srcDir = BASE_PATH;
const distDir = join(BASE_PATH, 'dist/apps/api');
const src = readFileSync(join(srcDir, 'package.json')).toString();
const dist = convertPackageJson(src);
if (!fs.existsSync(distDir)) fs.mkdirSync(distDir, {recursive: true});
writeFileSync(join(distDir, 'package.json'), dist);
}
function convertPackageJson(src: string): string {
const packageJson = JSON.parse(src);
delete packageJson['scripts'];
delete packageJson['devDependencies'];
packageJson['main'] = 'main.js';
packageJson['engines'] = {'node': '12'};
return JSON.stringify(packageJson, null , '\t');
}
main();
准备部署配置③(准备firebase.json转换脚本)
{
"hosting": {
...,
"rewrites": [
+ {
+ "source": "/api/**",
+ "function": "api"
+ },
{
"source": "**",
"destination": "/index.html"
}
]
},
+ "functions": {
+ "predeploy": [
+ "npx nx build api --prod",
+ "npx ts-node ./tools/scripts/generate-api-package-json.ts"
+ ],
+ "source": "dist/apps/api"
+ }
}
※ 重写将按照从上到下的顺序进行处理,所以要注意顺序!
第一个 hosting.rewrites 的配置是将对 https://.web.app/api/** 的请求重写为发送至 CloudFunctions 的 api 函数的请求。(示例中,https://.web.app/api/hello 会返回 NestJS 的 JSON。)
第二个 functions 是在部署 Cloud Functions 时的配置。
Firebase计划变更
要使用Cloud Functions for Firebase,需要升级到Blaze计划,并在Firebase控制台的“Functions”选项卡中进行启用。
Firebase 控制台
⑦ 部署
使用nx命令,根据angular.json中的配置进行部署。
// Deploy NestJS App
$ npx nx deploy api
// Deploy Angular App
$ npx nx deploy ng-pwa-sample
从浏览器进行确认。
当部署完成后,Firebase Hosting的网址会显示出来,访问并确认。
若在”Message”部分显示{“message”: “Welcome to api!”},则表示与API的通信成功。

未来的改进方向
将以下内容用中文进行转述,只需要一个选项:
Workflow的问题在于,我们只是使用Express来连接Cloud Functions和NestJS这两者之间的关系,并且第5个步骤非常复杂。我希望能够通过迁移到Fastify来解决前者的问题,而后者则可以通过使用Schematics进行自动化处理。如果能够自动化第5个步骤,我想应该可以在大约10分钟内完成环境的搭建。
附言
起初我对于是否能在Nx中运行Angular的Schematics感到担心,但实际上发现Nx本身就是Schematics的集合。我之前没有接触过NestJS,但它的感觉非常类似于Angular,看起来很高效。
请喜欢Angular的人务必尝试一下这个环境!