本指南详细介绍如何将 NestJS 应用部署到 CloudBase HTTP 云函数。
📋 前置要求:如果您还没有创建 NestJS 项目,请先阅读 NestJS 项目创建指南。
HTTP 云函数适合以下场景:
- 轻量级 API:RESTful API 服务、微服务
- 间歇性访问:不需要持续运行的应用
- 成本敏感:按请求次数和执行时间计费
- 快速部署:无需容器化配置
| 特性 | 说明 |
|---|---|
| 计费方式 | 按请求次数和执行时间 |
| 启动方式 | 冷启动,按需启动 |
| 端口要求 | 固定 9000 端口 |
| 扩缩容 | 自动按请求扩缩 |
| Node.js 环境 | 预配置 Node.js 运行时 |
创建 scf_bootstrap 文件(无扩展名):
#!/bin/bash
export PORT=9000
cd /var/user
node dist/main.js为启动脚本添加执行权限:
chmod +x scf_bootstrap确保 src/main.ts 支持云函数环境:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用 CORS
app.enableCors({
origin: true,
credentials: true,
});
// 配置端口
const port = process.env.PORT || 3000;
await app.listen(port, '0.0.0.0');
console.log(`NestJS application is running on port ${port}`);
}
bootstrap();# 构建生产版本
npm run build
# 确保 dist 目录存在
ls -la dist/确保 package.json 包含必要依赖:
{
"dependencies": {
"@nestjs/common": "^11.0.1",
"@nestjs/core": "^11.0.1",
"@nestjs/platform-express": "^11.0.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.3",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1"
}
}cloudrun-nestjs/
├── src/ # TypeScript 源代码
│ ├── main.ts
│ ├── app.module.ts
│ ├── app.controller.ts
│ ├── app.service.ts
│ ├── health/
│ └── users/
├── dist/ # 🔑 编译后的 JavaScript 代码
│ ├── main.js
│ └── ...
├── node_modules/ # 🔑 Node.js 依赖包
├── package.json # 项目配置和依赖
├── scf_bootstrap # 🔑 云函数启动脚本
└── nest-cli.json # NestJS CLI 配置
💡 说明:
scf_bootstrap是 CloudBase 云函数的启动脚本- 设置
PORT=9000环境变量确保应用监听云函数要求的端口- 重要:HTTP 云函数部署时需要包含
dist目录和node_modules目录- 使用云函数运行时环境的 Node.js 解释器启动应用
- 登录 CloudBase 控制台
- 选择您的环境,进入「云函数」页面
- 点击「新建云函数」
- 选择「HTTP 云函数」
- 填写函数名称(如:
cloudrun-nestjs-app) - 选择运行时:Node.js 18(或其他支持的版本)
- 提交方法选择:本地上传文件夹
- 函数代码选择
cloudrun-nestjs目录进行上传 - 自动安装依赖:开启此选项
- 点击「创建」按钮等待部署完成
如果需要手动打包:
# 构建项目
npm run build
# 创建部署包(包含 dist 和 node_modules)
zip -r cloudrun-nestjs-app.zip . -x ".git/*" "src/*" "test/*" "*.log" "Dockerfile" ".dockerignore" "node_modules/*"部署成功后,您可以参考通过 HTTP 访问云函数设置自定义域名访问 HTTP 云函数。
访问地址格式:https://your-function-url/
- 根路径:
/- NestJS 欢迎页面 - 健康检查:
/health- 查看应用状态 - 用户列表:
/api/users- 获取用户列表 - 用户详情:
/api/users/1- 获取特定用户 - 创建用户:
POST /api/users- 创建新用户
# 健康检查
curl https://your-function-url/health
# 获取用户列表
curl https://your-function-url/api/users
# 分页查询
curl "https://your-function-url/api/users?page=1&limit=2"
# 创建新用户
curl -X POST https://your-function-url/api/users \
-H "Content-Type: application/json" \
-d '{"name":"测试用户","email":"test@example.com"}'A: CloudBase HTTP 云函数要求应用监听 9000 端口,这是平台的标准配置。通过在 scf_bootstrap 中设置 PORT=9000 环境变量来控制端口,本地开发时默认使用 3000 端口。
A: 在云函数环境中,建议将静态文件托管到 CDN 或对象存储。如果必须在应用中处理静态文件,可以配置 NestJS 的静态文件服务:
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// 配置静态文件
app.useStaticAssets(join(__dirname, '..', 'public'));
// ...
}A: 在 main.ts 中配置 CORS:
app.enableCors({
origin: ['https://yourdomain.com'], // 生产环境应该设置具体域名
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
});A: HTTP 云函数部署时需要包含构建后的 dist 目录和 node_modules 目录。确保在部署前运行 npm run build 构建项目。
A: 在 CloudBase 控制台的云函数页面,点击函数名称进入详情页查看运行日志。
A: CloudBase 支持 Node.js 14、16、18、20 等版本,建议使用最新的 LTS 版本。
// config/configuration.ts
export default () => ({
port: parseInt(process.env.PORT, 10) || 3000,
database: {
host: process.env.DATABASE_HOST,
port: parseInt(process.env.DATABASE_PORT, 10) || 5432,
},
});// app.module.ts
import { ConfigModule } from '@nestjs/config';
import configuration from './config/configuration';
@Module({
imports: [
ConfigModule.forRoot({
load: [configuration],
}),
],
})
export class AppModule {}增强 scf_bootstrap 脚本:
#!/bin/bash
export PORT=9000
export NODE_ENV=production
# 检查构建文件
if [ ! -d "dist" ]; then
echo "Build directory not found"
exit 1
fi
# 启动应用
cd /var/user
node dist/main.js// filters/http-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
} from '@nestjs/common';
import { Request, Response } from 'express';
@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response.status(status).json({
success: false,
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message: exception.message,
});
}
}// interceptors/logging.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
Logger,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
private readonly logger = new Logger(LoggingInterceptor.name);
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const request = context.switchToHttp().getRequest();
const method = request.method;
const url = request.url;
const now = Date.now();
return next.handle().pipe(
tap(() => {
const response = context.switchToHttp().getResponse();
const delay = Date.now() - now;
this.logger.log(
`${method} ${url} ${response.statusCode} - ${delay}ms`,
);
}),
);
}
}// main.ts
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 全局验证管道
app.useGlobalPipes(
new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}),
);
// ...
}-
scf_bootstrap文件存在且有执行权限 - 端口配置为 9000
- 项目已构建(
dist目录存在) -
package.json包含所有必需依赖 - 包含
dist目录和node_modules目录 - 排除不必要的文件(如
src、test、Dockerfile) - 测试本地构建和启动是否正常
- 检查启动脚本语法是否正确
// 使用懒加载模块
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
cache: true,
}),
],
})
export class AppModule {}# 生产构建优化
npm run build
# 清理开发依赖
npm prune --production
# 清理不必要的文件
rm -rf src test *.md// guards/memory.guard.ts
import { Injectable, CanActivate, ExecutionContext, Logger } from '@nestjs/common';
@Injectable()
export class MemoryGuard implements CanActivate {
private readonly logger = new Logger(MemoryGuard.name);
canActivate(context: ExecutionContext): boolean {
const memUsage = process.memoryUsage();
this.logger.log(`Memory usage: ${Math.round(memUsage.rss / 1024 / 1024)} MB`);
return true;
}
}// interceptors/transform.interceptor.ts
import {
Injectable,
NestInterceptor,
ExecutionContext,
CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, any> {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({
success: true,
data,
timestamp: new Date().toISOString(),
})),
);
}
}