云开发下部署 Nest.js 项目并集成 Fastify:攻克 Node 版本与云函数调用难题

299 阅读4分钟

背景

作为一名经验丰富的前端开发者,我总喜欢尝试和探索新事物。多年前,我购买了一台云服务器,并搭建了一个博客系统。回想当时的过程,我需要处理 登录鉴权数据库建表服务器安全 等一系列繁琐的任务,而这些并非我所擅长的领域。

不幸的是,我的系统后来遭到了黑客攻击,Mogodb 数据库中的数据被删除,黑客还留下了一段要求支付 1 个比特币赎金的信息。这让我意识到,运维等工作确实需要专业人士来处理,而我作为业余爱好者在这方面的知识有限。因此,我一直在寻找一个免运维、安全且易于操作的平台来搭建博客。最终,我选择了腾讯云的云开发,这个平台上手简单,非常适合个人开发者使用。

本文将介绍如何利用云开发 CloudBase 部署 Nest.js 项目,并集成 Fastify。在这个过程中,我遇到了一些问题,希望本文的解决方案能为大家在类似场景下的开发提供帮助。

PS: 这些问题,即使是云开发的开发人员也未能解决。

方案

  1. 利用云开发的云函数部署 Node 项目,并开通外网 HTTP 访问
  2. 前端通过云开发的 Web SDK 发起 HTTP 请求,调用云函数的接口(这样做可以天然 免鉴权
  3. 数据存储在云开发的数据库中,Node 项目中通过 云开发的 Node SDK 读写数据库

问题梳理

  1. 在云开发控制台无法直接部署 Nestjs 框架的云函数,需要用 CloudBase CLI 来创建

  2. CloudBase CLI 最高只支持创建 Node 版本为 12.16 的云函数,但是新版的 Nestjs 的 Node 版本最低 16

image.png

image.png

  1. 创建好的 Nestjs 云函数是不能直接用,要进行代码改造

  2. 通过 @cloudbase/js-sdk 无法通过 POST 请求调用 Nestjs 框架的云函数(如果改用 request 的方式,就需要处理鉴权)

image.png

解决方案

前期准备

安装必要的依赖,这里也可以直接参考官方文档:CloudBase CLI 使用指南Web 端 SDK

  • 前端项目中,安装 Web 端 SDK
npm install @cloudbase/js-sdk -S
  • 电脑终端安装 CloudBase CLI
npm install -g @cloudbase/cli

部署 Nestjs 框架的云函数

  1. 首先,我们需要在腾讯云控制台手动创建一个 Node.js 16.13 的云函数。为我们部署 Nestjs 提供基础(这里记住函数名称,待会用的上)

查看图片

  1. 使用 CloudBase CLI 创建同名云函数

直接用 CloudBase CLI 命令创建出来的 Nest.js 项目不是最新的版本,如果需要最新的 Nest.js 可以先用 Nest 官方脚手架创建项目

npm i -g @nestjs/cli
nest new project-name

现在我们开始改造项目

修改 main.ts 文件

为了让我们的 Nest.js 项目能够与云函数无缝对接,我们需要对 main.ts 文件进行一些修改。具体修改方法如下:

  1. 将 NestFactory.create 方法的参数从 AppModule 改为 AppModule, {adapter: new FastifyAdapter()},以使用 Fastify 作为 HTTP 服务器。
  2. 将 app.listen 改为 app.init,因为云函数不需要监听端口。
  3. 在 app.init 之前,添加 app.setGlobalPrefix('api'),以设置全局路由前缀。
import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

const port = process.env.PORT || 9000;

export async function bootstrap() {
  const adapter = new FastifyAdapter();

  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    adapter,
  );

  let fastifyApp = null;

  // 兼容云函数与本地开发
  if (process.env.NODE_ENV === 'development') {
    await app.listen(port);
  } else {
    fastifyApp = await app.init();
  }

  return fastifyApp;
}

// 开发模式下启动开发
if (process.env.NODE_ENV === 'development') {
  bootstrap().then(() => {
    console.log(`App listen on http://localhost:${port}`);
  });
}

添加云函数所需配置

  • 项目根目录下新增 app.js 文件
/* eslint-disable */
const main = require('./dist/src/main')

exports.tcbGetApp = main.bootstrap
  • 项目根目录下新增 cloudbaserc.json 云函数配置文件,可以参考官方介绍
{
  "version": "2.0",
  "envId": "<云开发环境id>",
  "$schema": "https://framework-1258016615.tcloudbaseapp.com/schema/latest.json",
  "framework": {
    "name": "<函数名称(`需要和之前创建的云函数名称一样`)>",
    "plugins": {
      "node": {
        "use": "@cloudbase/framework-plugin-node",
        "inputs": {
          "name": "<函数名称>",
          "path": "<自定义访问路径>",
          "entry": "app.js",
          "buildCommand": "npm install --prefer-offline --no-audit --progress=false && npm run build",
          "functionOptions": {
            "timeout": 5,
            "envVariables": {
              "NODE_ENV": "production"
            },
            "memorySize": "256"
          }
        }
      }
    }
  },
  "functionRoot": "./functions",
  "functions": [],
  "region": "<函数所在地域>"
}

  • 修改打包配置 package.json

记得要安装 @cloudbase/cli,不然会找不到 tcb 命令

// 新增一条部署命令
"scripts" {
    // ...
    "deploy": "tcb framework deploy"
}

接下来,我们需要运行npm run deploy 命令部署同名云函数

设置 @cloudbase/js-sdk 的请求头

前端在使用 @cloudbase/js-sdk 请求部署 Nest.js 的云函数时,我们需要设置请求头,以解决无法调用 POST 请求的问题。具体设置方法如下:

import cloudbase from '@cloudbase/js-sdk';

// 封装 tcb 云函数请求
export const tcbRequest = async ({ path = '', data = {} }) => {
  // 参数拼接
  const queryString = Object.entries(data)
    .map(([key, value]) => `${key}=${value}`)
    .join('&');

  const app = cloudbase.init({
    env: 'xxx', // 您的环境id
  })
  
  const { result } = await app.callFunction({
    name: 'scf-name',
    data: {
      path,
      httpMethod: 'POST',
      isBase64Encoded: false,
      body: queryString,
      headers: {
        'content-type': 'application/x-www-form-urlencoded',
      },
    },
  });

  // 返回结果解析
  const { body } = result || {};
  const response = JSON.parse(body || '{}');
  
  if (response.status === 0) {
    return response.data;
  }

  console.error(JSON.stringify(response.message));

  return null;
};

验证

image.png

image.png

可以看到我调用的登录,Web SDK 自动处理 tokenrefresh_token 相关的逻辑。调用业务接口,云函数也能正常返回数据,不过数据是序列化之后的,前端只需要反序列化就可以了。是不是很方便?

结束

通过以上几个步骤,我们成功地将 Nest.js 项目部署到了腾讯云云开发,并集成了 Fastify。同时,我们也解决了 Node 版本和云函数调用的问题。希望本文对大家在类似场景下的开发有所帮助。

欢迎大家 点赞 + 评论 + 收藏