跳到主要内容
Zomer Gregorio

Zomer Gregorio

软件工程师

简历
前端
  • TypeScript
  • React
  • Next.js
  • TanStack
  • Tailwind CSS
后端
  • Node.js
  • Hono
  • Express
  • Django
数据
  • PostgreSQL
  • Redis
  • Drizzle
  • Prisma
基础设施/工具
  • Docker
  • GitHub Actions
  • Cloudflare
  • Vercel
博客

Integrating NestJS, GCP, and Replicate API for AI-Powered SSR

· nestjs · gcp · replicate-api · ssr · typescript

Build a robust, server-side rendered application with NestJS, powered by Google Cloud Platform and Replicate API for advanced AI functionalities. Learn development, deployment, testing, and debugging with TypeScript.

获取更新

每当我发布新内容时,你会收到一条简短通知。你的电子邮件或浏览器订阅信息仅用于发送这些更新,并可随时取消订阅;无需账户或跟踪档案。

开始接收通知前,需要通过确认邮件完成验证。

Introduction: Unifying Modern Web Stacks

In today's dynamic web landscape, integrating diverse technologies to create powerful, responsive applications is paramount. This article demonstrates how to weave together NestJS for a scalable backend, Google Cloud Platform (GCP) for robust deployment, and the Replicate API to infuse cutting-edge AI capabilities, all while delivering an enhanced user experience through Server-Side Rendering (SSR). We'll build a simple image generation application as our example, showcasing a practical, TypeScript-first approach.

NestJS Core: The Backend Foundation

NestJS, a progressive Node.js framework, provides a solid architectural foundation inspired by Angular. It promotes modularity, testability, and maintainability. Let's set up a new project and configure a templating engine like Handlebars for SSR.

First, initialize your project:

npm i -g @nestjs/cli
nest new ai-ssr-app --strict --product --package-manager npm
cd ai-ssr-app
npm install --save @nestjs/platform-express express-handlebars
npm install --save-dev @types/express-handlebars

Configure src/main.ts to use Handlebars:

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import * as exphbs from 'express-handlebars';
import { AppModule } from './app.module';
 
async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
 
  app.setBaseViewsDir(join(__dirname, '..', 'views'));
  app.engine(
    'hbs',
    exphbs.create({
      extname: '.hbs',
      layoutsDir: join(__dirname, '..', 'views/layouts'),
      defaultLayout: 'main',
    }).engine,
  );
  app.setViewEngine('hbs');
 
  await app.listen(process.env.PORT || 3000);
}
bootstrap();

Create a views directory with views/layouts/main.hbs and views/index.hbs.

Integrating Replicate API for AI Magic

The Replicate API offers access to hundreds of AI models. We'll use it to generate images. For security, we'll store our API key in GCP Secret Manager.

1. GCP Secret Manager: Enable the Secret Manager API in your GCP project. Store your Replicate API token (from replicate.com/account/api-tokens) as a secret, e.g., REPLICATE_API_TOKEN.

2. NestJS Service for Replicate Interaction: Create src/replicate/replicate.service.ts to encapsulate the Replicate API calls. Install replicate and @nestjs/config:

npm install replicate @nestjs/config
npm install --save-dev @types/replicate
// src/replicate/replicate.service.ts
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import Replicate from 'replicate';
 
@Injectable()
export class ReplicateService {
  private readonly replicate: Replicate;
  private readonly logger = new Logger(ReplicateService.name);
 
  constructor(private configService: ConfigService) {
    const replicateApiToken = this.configService.get<string>('REPLICATE_API_TOKEN');
    if (!replicateApiToken) {
      throw new Error('REPLICATE_API_TOKEN is not configured.');
    }
    this.replicate = new Replicate({ auth: replicateApiToken });
  }
 
  async generateImage(prompt: string): Promise<string[]> {
    try {
      this.logger.log(`Generating image for prompt: "${prompt}"`);
      const output = await this.replicate.run(
        "stability-ai/sdxl:39ed52f2a7839b6838adbe55e4e0a5e378cadd45826f7779836f663c0a87699", // Stable Diffusion XL
        { input: { prompt: prompt, num_outputs: 1 } }
      );
      return output as string[];
    } catch (error) {
      this.logger.error('Error generating image:', error.message);
      throw new InternalServerErrorException('Failed to generate image.');
    }
  }
}

Remember to import ConfigModule and ReplicateModule into your AppModule.

Server-Side Rendering (SSR) with NestJS Views

Our AppController will render the initial page and expose an API endpoint for image generation.

// src/app.controller.ts
import { Controller, Get, Render, Post, Body, Res } from '@nestjs/common';
import { Response } from 'express';
import { ReplicateService } from './replicate/replicate.service';
 
interface GenerateImageDto { prompt: string; }
 
@Controller()
export class AppController {
  constructor(private readonly replicateService: ReplicateService) {}
 
  @Get()
  @Render('index') // Renders views/index.hbs
  root() {
    return { message: 'Enter a prompt to generate an image!' };
  }
 
  @Post('generate')
  async generateImage(@Body() body: GenerateImageDto, @Res() res: Response) {
    try {
      const imageUrls = await this.replicateService.generateImage(body.prompt);
      return res.json({ success: true, imageUrl: imageUrls[0] });
    } catch (error) {
      return res.status(500).json({ success: false, message: error.message });
    }
  }
}

Deployment to Google Cloud Run

GCP Cloud Run is a fully managed platform for containerized applications, scaling automatically and costing only when your code runs.

1. Dockerize Your NestJS Application: Create a Dockerfile in your project root.

FROM node:20-slim as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN npm run build
 
FROM node:20-slim
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/views ./views
EXPOSE 3000
CMD [ "node", "dist/main" ]

2. Deploy with gcloud: Ensure the gcloud CLI is installed and configured. Replace [PROJECT_ID] with your GCP project ID.

gcloud builds submit --tag gcr.io/[PROJECT_ID]/ai-ssr-app
gcloud run deploy ai-ssr-app \
  --image gcr.io/[PROJECT_ID]/ai-ssr-app \
  --platform managed \
  --region us-central1 \
  --allow-unauthenticated \
  --set-secrets REPLICATE_API_TOKEN=REPLICATE_API_TOKEN:latest

This deploys your application, making it publicly accessible and securely injecting the REPLICATE_API_TOKEN from Secret Manager.

Robustness: Testing & Debugging Strategies

1. Unit Testing with Jest: NestJS heavily leverages Jest. Mock external dependencies like the Replicate client to isolate your service logic.

// src/replicate/replicate.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { ConfigService } from '@nestjs/config';
import { ReplicateService } from './replicate.service';
 
const mockReplicate = { run: jest.fn(() => Promise.resolve(['https://example.com/image.png'])) };
 
describe('ReplicateService', () => {
  let service: ReplicateService;
 
  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ReplicateService,
        { provide: ConfigService, useValue: { get: jest.fn(() => 'test-token') } },
      ],
    }).compile();
 
    service = module.get<ReplicateService>(ReplicateService);
    (service as any).replicate = mockReplicate; // Inject mock
  });
 
  it('should generate an image via Replicate API', async () => {
    const result = await service.generateImage('a futuristic city');
    expect(mockReplicate.run).toHaveBeenCalledTimes(1);
    expect(result).toEqual(['https://example.com/image.png']);
  });
});

2. End-to-End (E2E) Testing with Supertest: Test the full request lifecycle using Supertest provided by NestJS.

// test/app.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
 
describe('AppController (e2e)', () => {
  let app: INestApplication;
 
  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [AppModule] }).compile();
    app = moduleFixture.createNestApplication();
    await app.init();
  });
 
  it('/ (GET) should render the index page', () => {
    return request(app.getHttpServer()).get('/').expect(200).expect('Content-Type', /text\/html/);
  });
 
  afterAll(async () => { await app.close(); });
});

3. Debugging NestJS:

  • VS Code Debugger: Configure launch.json for Node.js debugging. Run your app with node --inspect-brk dist/main.js and attach the debugger.
  • Cloud Run Logging: Cloud Run integrates with Cloud Logging. Use NestJS's Logger for structured logging, which is easily searchable and analyzable in GCP.

Conclusion: A Synergistic Stack

By integrating NestJS, GCP, and the Replicate API, we've demonstrated how to build a powerful, scalable, and intelligent application with a modern SSR frontend. This architecture provides a robust backend, leverages cutting-edge AI, ensures secure deployment, and offers the essential tools for testing and debugging. As you expand, consider incorporating WebSockets for real-time AI feedback, Cloud Storage for persistent asset management, and more sophisticated frontend frameworks for interactivity, all while maintaining the core principles demonstrated here.