Skip to content
← Back to blog
NestJSAngularSaaS中文指南TypeScript全栈开发

使用NestJS和Angular构建SaaS应用:完整指南2026

Firas Sayah·July 18, 2026·10 min read

你是否正在考虑构建自己的SaaS产品,却被复杂的基础设施吓退了? 🚀 你并不孤单。数以千计的中国开发者每天都在面对同样的困境——在认证、支付、多租户等基础模块上花费数周甚至数月,却还没有写一行真正的业务代码。

好消息是,有更聪明的方式来起步。这篇指南将带你了解如何用最高效的方式构建一个生产级SaaS应用。

现代软件开发工作空间

为什么选择NestJS + Angular构建SaaS?

在2026年的SaaS开发领域,技术栈的选择直接决定了产品的开发效率和长期维护成本。NestJS和Angular的组合提供了一个独特的优势:前后端统一使用TypeScript,共享类型定义和数据验证逻辑

对于中国开发者来说,这个组合还有一个实际的好处——Angular在企业级应用中的广泛采用意味着更容易招聘到有经验的前端开发者,而NestJS的模块化架构与Spring Boot非常相似,Java背景的后端开发者可以快速上手。

💡 NestJS vs Next.js? 如果你正在两者之间犹豫,请阅读我们的深度对比分析,了解各自在SaaS场景中的优劣势。

本指南将从零开始,完整讲解如何使用SaaS Starter构建一个生产级的SaaS应用。

项目架构概览

一个生产级的SaaS应用需要以下核心模块:

saas-project/
  apps/
    api/          # NestJS后端API
    web/          # Angular前端应用
    worker/       # BullMQ后台任务处理
  libs/
    shared/       # 前后端共享的DTO和接口
    database/     # TypeORM实体和迁移
    auth/         # 认证模块
    billing/      # 支付集成模块
    tenancy/      # 多租户模块
  infra/
    terraform/    # AWS基础设施代码
    docker/       # Docker配置

使用Nx monorepo管理所有应用和库,确保代码共享和构建优化。

Cloudrix SaaS Starter ships with this pre-configured and tested.

Skip weeks of boilerplate — auth, payments, multi-tenancy, and deployment included out of the box.

Try the live demo →

认证系统实现

SaaS应用的认证不仅仅是登录和注册。你需要支持JWT令牌、OAuth社交登录、双因素认证(2FA)基于角色的访问控制(RBAC)

JWT认证

// libs/auth/src/jwt.strategy.ts
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    private readonly configService: ConfigService,
    private readonly usersService: UsersService,
  ) {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      secretOrKey: configService.get('JWT_SECRET'),
    });
  }

  async validate(payload: JwtPayload): Promise<User> {
    const user = await this.usersService.findById(payload.sub);
    if (!user || !user.isActive) {
      throw new UnauthorizedException('用户不存在或已被禁用');
    }
    return user;
  }
}

OAuth社交登录

支持Google、GitHub和微信登录对于面向中国市场的SaaS尤为重要:

// apps/api/src/auth/strategies/wechat.strategy.ts
@Injectable()
export class WeChatStrategy extends PassportStrategy(Strategy, 'wechat') {
  constructor(private readonly configService: ConfigService) {
    super({
      appID: configService.get('WECHAT_APP_ID'),
      appSecret: configService.get('WECHAT_APP_SECRET'),
      callbackURL: configService.get('WECHAT_CALLBACK_URL'),
      scope: 'snsapi_login',
    });
  }

  async validate(accessToken: string, profile: WeChatProfile) {
    return this.authService.findOrCreateFromOAuth({
      provider: 'wechat',
      providerId: profile.openid,
      displayName: profile.nickname,
      avatar: profile.headimgurl,
    });
  }
}

基于角色的访问控制

// 定义权限装饰器
@Controller('organizations')
export class OrganizationsController {
  @Get()
  @Roles(Role.Admin, Role.Manager)
  @UseGuards(JwtAuthGuard, RolesGuard)
  async findAll(@CurrentTenant() tenantId: string) {
    return this.organizationsService.findByTenant(tenantId);
  }

  @Delete(':id')
  @Roles(Role.Admin)
  @UseGuards(JwtAuthGuard, RolesGuard)
  async remove(@Param('id') id: string) {
    return this.organizationsService.remove(id);
  }
}

云端服务器基础设施

多租户架构

多租户是SaaS的核心。SaaS Starter使用PostgreSQL的schema隔离方案,每个租户拥有独立的数据库schema,从根本上防止数据泄露。了解更多请阅读我们的多租户架构深度解析

// libs/tenancy/src/tenant.middleware.ts
@Injectable()
export class TenantMiddleware implements NestMiddleware {
  constructor(private readonly dataSource: DataSource) {}

  async use(req: Request, res: Response, next: NextFunction) {
    const tenantId = this.extractTenantId(req);

    if (!tenantId) {
      throw new BadRequestException('缺少租户标识');
    }

    // 切换到租户的schema
    const schemaName = `tenant_${tenantId}`;
    await this.dataSource.query(
      `SET search_path TO "${schemaName}", public`
    );

    req['tenantId'] = tenantId;
    next();
  }

  private extractTenantId(req: Request): string | null {
    // 支持子域名方式: company1.yourapp.com
    const host = req.headers.host;
    const subdomain = host?.split('.')[0];
    if (subdomain && subdomain !== 'www' && subdomain !== 'api') {
      return subdomain;
    }
    // 或者从请求头获取
    return req.headers['x-tenant-id'] as string;
  }
}

创建新租户

// libs/tenancy/src/tenant.service.ts
@Injectable()
export class TenantService {
  async createTenant(name: string, adminUser: CreateUserDto) {
    const tenantId = generateSlug(name);
    const schemaName = `tenant_${tenantId}`;

    // 创建schema
    await this.dataSource.query(
      `CREATE SCHEMA IF NOT EXISTS "${schemaName}"`
    );

    // 在新schema中运行迁移
    await this.runMigrations(schemaName);

    // 创建管理员用户
    await this.dataSource.query(`SET search_path TO "${schemaName}"`);
    await this.usersService.create({
      ...adminUser,
      role: Role.Admin,
    });

    return { tenantId, schemaName };
  }
}

Stripe支付集成

SaaS应用的收入来自订阅。以下是如何集成Stripe实现订阅管理

// libs/billing/src/stripe.service.ts
@Injectable()
export class StripeService {
  private stripe: Stripe;

  constructor(private configService: ConfigService) {
    this.stripe = new Stripe(configService.get('STRIPE_SECRET_KEY'));
  }

  async createSubscription(
    customerId: string,
    priceId: string,
  ): Promise<Stripe.Subscription> {
    return this.stripe.subscriptions.create({
      customer: customerId,
      items: [{ price: priceId }],
      payment_behavior: 'default_incomplete',
      expand: ['latest_invoice.payment_intent'],
    });
  }

  async handleWebhook(payload: Buffer, signature: string) {
    const event = this.stripe.webhooks.constructEvent(
      payload,
      signature,
      this.configService.get('STRIPE_WEBHOOK_SECRET'),
    );

    switch (event.type) {
      case 'customer.subscription.updated':
        await this.handleSubscriptionUpdate(event.data.object);
        break;
      case 'invoice.payment_failed':
        await this.handlePaymentFailed(event.data.object);
        break;
    }
  }
}

🔥 你知道吗? 从零搭建认证、支付、多租户和部署基础设施需要8-14周,成本高达$24,000-$84,000。查看完整成本分析。使用 Cloudrix SaaS Starter,这一切在第一天就已就绪——立即体验免费Demo! 🎯

后台任务处理

使用BullMQ处理耗时操作,如发送邮件、生成报表和处理文件上传:

// apps/worker/src/processors/report.processor.ts
@Processor('reports')
export class ReportProcessor {
  private readonly logger = new Logger(ReportProcessor.name);

  @Process('generate-monthly')
  async generateMonthlyReport(job: Job<ReportJobData>) {
    this.logger.log(`正在为租户 ${job.data.tenantId} 生成月度报表`);

    const data = await this.analyticsService.getMonthlyData(
      job.data.tenantId,
      job.data.month,
    );

    const pdfBuffer = await this.pdfService.generate(data);

    await this.storageService.upload(
      `reports/${job.data.tenantId}/${job.data.month}.pdf`,
      pdfBuffer,
    );

    await this.emailService.send({
      to: job.data.recipientEmail,
      subject: `${job.data.month} 月度报表已生成`,
      template: 'monthly-report',
      context: { downloadUrl: `...` },
    });
  }
}

Angular前端实现

状态管理

使用Angular Signals进行响应式状态管理:

// apps/web/src/app/core/auth.store.ts
@Injectable({ providedIn: 'root' })
export class AuthStore {
  private readonly http = inject(HttpClient);

  // Signals
  currentUser = signal<User | null>(null);
  isAuthenticated = computed(() => !!this.currentUser());
  isAdmin = computed(() =>
    this.currentUser()?.role === Role.Admin
  );

  async login(credentials: LoginDto): Promise<void> {
    const response = await firstValueFrom(
      this.http.post<AuthResponse>('/api/auth/login', credentials)
    );
    localStorage.setItem('token', response.accessToken);
    this.currentUser.set(response.user);
  }

  logout(): void {
    localStorage.removeItem('token');
    this.currentUser.set(null);
  }
}

租户切换组件

// apps/web/src/app/shared/tenant-switcher.component.ts
@Component({
  selector: 'app-tenant-switcher',
  template: `
    <select [ngModel]="currentTenant()" (ngModelChange)="switchTenant($event)">
      @for (tenant of tenants(); track tenant.id) {
        <option [value]="tenant.id">{{ tenant.name }}</option>
      }
    </select>
  `,
})
export class TenantSwitcherComponent {
  private tenantService = inject(TenantService);

  tenants = this.tenantService.availableTenants;
  currentTenant = this.tenantService.currentTenantId;

  switchTenant(tenantId: string) {
    this.tenantService.setCurrentTenant(tenantId);
    window.location.reload();
  }
}

团队协作开发场景

部署到生产环境

SaaS Starter提供完整的DockerTerraform配置

# 本地开发
docker-compose up -d

# 部署到AWS
cd infra/terraform/environments/production
terraform init
terraform plan
terraform apply

生产架构包括:

  • ECS Fargate — 运行NestJS API,自动伸缩
  • S3 + CloudFront — 全球CDN分发Angular前端
  • RDS PostgreSQL — 托管数据库,自动备份
  • ElastiCache Redis — 会话存储和BullMQ队列
  • CloudWatch — 监控和告警

快速开始 🎯

不需要从零搭建这一切。SaaS Starter已经将上述所有功能打包成一个即用的模板。

  1. 访问 demo.cloudrix.io 体验免费精简版
  2. 克隆项目并运行 docker-compose up
  3. 在几分钟内拥有一个完整的SaaS应用骨架

免费精简版包含核心的认证、多租户和基础UI。专业版额外提供Terraform部署、Stripe集成、高级RBAC、审计日志后台任务处理系统

想比较不同的模板?阅读我们的Cloudrix vs ShipFast vs Supastarter对比

立即开始你的SaaS之旅

无论你是独立开发者还是企业团队,NestJS + Angular的组合都能为你的SaaS产品提供坚实的技术基础。

不要再浪费时间重复造轮子了。 立即体验Cloudrix SaaS Starter — 从免费精简版开始,或以一次性付费 $249 获取专业版。将节省下来的2-3个月投入到让你的产品与众不同的核心功能上! 🚀

F

Firas Sayah

Senior Software Engineer

Full-stack developer with 5+ years building production SaaS applications with NestJS and Angular.