Generate NestJS gRPC Microservices with AI in 12 Minutes

Use Claude or ChatGPT to generate production-ready NestJS gRPC boilerplate with proper proto files, controllers, and type safety.

Problem: Setting Up gRPC Microservices Takes Hours

You need to build a NestJS microservice with gRPC, but manually creating proto files, generating TypeScript types, configuring services, and wiring up controllers eats your entire morning.

You'll learn:

  • How to prompt AI to generate complete gRPC boilerplate
  • Proper proto file structure for production use
  • NestJS-specific patterns AI often gets wrong
  • Validation and testing setup from the start

Time: 12 min | Level: Intermediate


Why This Happens

gRPC requires coordinating multiple pieces: proto definitions, code generation, NestJS decorators, and type safety. Most tutorials show toy examples, not production patterns with proper error handling and validation.

Common symptoms:

  • Spending 2+ hours on boilerplate before writing business logic
  • Proto files that don't match TypeScript interfaces
  • Missing error handling in gRPC interceptors
  • No validation on incoming messages

Solution

Step 1: Prompt AI with Context

Instead of asking "create a NestJS gRPC service," provide structure:

Create a NestJS gRPC microservice with:

**Service:** User management (CRUD operations)
**Tech Stack:**
- NestJS 10.x
- @grpc/grpc-js (not grpc package)
- class-validator for DTOs
- Jest for testing

**Requirements:**
1. Proto file with proper package naming
2. Generated TypeScript interfaces
3. Service implementation with error handling
4. Controller with validation
5. Health check endpoint
6. Docker setup for local testing

**File structure:**
src/
├── proto/
│   └── user.proto
├── user/
│   ├── user.service.ts
│   ├── user.controller.ts
│   └── dto/
└── main.ts

Use realistic examples (not id=1, name="John").

Why this works: AI has context about your stack, sees the full structure, and knows to avoid deprecated packages.


Step 2: Review and Fix Common AI Mistakes

AI-generated code often has these issues:

❌ Wrong package import:

// AI often generates (deprecated)
import * as grpc from 'grpc';

// ✅ Correct for 2026
import * as grpc from '@grpc/grpc-js';
import { credentials } from '@grpc/grpc-js';

❌ Missing validation:

// AI skips validation
async createUser(data: CreateUserRequest) {
  return this.userService.create(data);
}

// ✅ Add class-validator
import { IsEmail, IsNotEmpty, Length } from 'class-validator';

class CreateUserDto {
  @IsNotEmpty()
  @Length(2, 50)
  name: string;

  @IsEmail()
  email: string;
}

❌ No error mapping:

// AI returns generic errors
throw new Error('User not found');

// ✅ Use gRPC status codes
import { RpcException } from '@nestjs/microservices';
import { status } from '@grpc/grpc-js';

throw new RpcException({
  code: status.NOT_FOUND,
  message: 'User not found'
});

Step 3: Generate Proto File with AI

Prompt for production-ready proto:

Create a user.proto file for NestJS gRPC with:
- Proper package naming (com.company.user.v1)
- Request/response messages with validation hints
- Service definition with all CRUD operations
- Comments for code generation tools
- Timestamp and error handling patterns

Follow Google's API design guide.

Expected output:

syntax = "proto3";

package com.company.user.v1;

import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

// User service for managing user accounts
service UserService {
  // Create a new user
  rpc CreateUser(CreateUserRequest) returns (UserResponse);
  
  // Get user by ID
  rpc GetUser(GetUserRequest) returns (UserResponse);
  
  // List users with pagination
  rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
  
  // Update user details
  rpc UpdateUser(UpdateUserRequest) returns (UserResponse);
  
  // Delete user (soft delete)
  rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
}

message CreateUserRequest {
  string name = 1;      // Min 2, max 50 chars
  string email = 2;     // Valid email format
  string role = 3;      // ENUM: admin, user, guest
}

message GetUserRequest {
  string id = 1;        // UUID format
}

message UpdateUserRequest {
  string id = 1;
  optional string name = 2;
  optional string email = 3;
}

message DeleteUserRequest {
  string id = 1;
}

message ListUsersRequest {
  int32 page = 1;       // Default: 1
  int32 page_size = 2;  // Default: 10, max: 100
  string filter = 3;    // Optional search query
}

message UserResponse {
  string id = 1;
  string name = 2;
  string email = 3;
  string role = 4;
  google.protobuf.Timestamp created_at = 5;
  google.protobuf.Timestamp updated_at = 6;
}

message ListUsersResponse {
  repeated UserResponse users = 1;
  int32 total = 2;
  int32 page = 3;
  int32 page_size = 4;
}

Step 4: Configure NestJS with Generated Code

Ask AI to create the main configuration:

Create main.ts for NestJS gRPC microservice using the user.proto file.
Include:
- Proper gRPC server options
- Proto path resolution
- Graceful shutdown
- Error logging

Generated main.ts:

import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { join } from 'path';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.createMicroservice<MicroserviceOptions>(
    AppModule,
    {
      transport: Transport.GRPC,
      options: {
        package: 'com.company.user.v1',
        protoPath: join(__dirname, '../proto/user.proto'),
        url: '0.0.0.0:50051',
        loader: {
          keepCase: true,
          longs: String,
          enums: String,
          defaults: true,
          oneofs: true,
        },
      },
    },
  );

  // Graceful shutdown
  app.enableShutdownHooks();

  await app.listen();
  console.log('gRPC server running on 0.0.0.0:50051');
}

bootstrap();

Why these options matter:

  • keepCase: true - Preserves proto field names (don't convert to camelCase)
  • longs: String - Avoids BigInt issues in JavaScript
  • enableShutdownHooks() - Properly closes connections on SIGTERM

Step 5: Add Validation Layer

AI rarely generates proper validation. Prompt specifically:

Create a NestJS pipe for validating gRPC requests using class-validator.
Handle these cases:
- Transform proto messages to DTOs
- Validate with class-validator decorators
- Return gRPC status codes on validation errors
- Log validation failures

Generated validation pipe:

import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import { RpcException } from '@nestjs/microservices';
import { plainToClass } from 'class-transformer';
import { validate } from 'class-validator';
import { status } from '@grpc/grpc-js';

@Injectable()
export class GrpcValidationPipe implements PipeTransform {
  async transform(value: any, metadata: ArgumentMetadata) {
    if (!metadata.metatype || !this.toValidate(metadata.metatype)) {
      return value;
    }

    const object = plainToClass(metadata.metatype, value);
    const errors = await validate(object);

    if (errors.length > 0) {
      const messages = errors.map(err => 
        Object.values(err.constraints || {}).join(', ')
      );
      
      throw new RpcException({
        code: status.INVALID_ARGUMENT,
        message: `Validation failed: ${messages.join('; ')}`,
      });
    }

    return object;
  }

  private toValidate(metatype: Function): boolean {
    const types: Function[] = [String, Boolean, Number, Array, Object];
    return !types.includes(metatype);
  }
}

Use in controller:

import { Controller, UsePipes } from '@nestjs/common';
import { GrpcMethod } from '@nestjs/microservices';

@Controller()
export class UserController {
  constructor(private readonly userService: UserService) {}

  @GrpcMethod('UserService', 'CreateUser')
  @UsePipes(new GrpcValidationPipe())
  async createUser(data: CreateUserDto): Promise<UserResponse> {
    // Validation happens before this executes
    return this.userService.create(data);
  }
}

Step 6: Generate Testing Setup

Final prompt for AI:

Create Jest test setup for the NestJS gRPC UserService including:
- Mock gRPC client
- Test for successful user creation
- Test for validation errors
- Test for NOT_FOUND error handling
Use realistic test data.

Generated test file:

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { RpcException } from '@nestjs/microservices';
import { status } from '@grpc/grpc-js';

describe('UserService', () => {
  let service: UserService;
  let repository: Repository<User>;

  const mockRepository = {
    create: jest.fn(),
    save: jest.fn(),
    findOne: jest.fn(),
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useValue: mockRepository,
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    repository = module.get<Repository<User>>(getRepositoryToken(User));
  });

  describe('createUser', () => {
    it('should create a user successfully', async () => {
      const createDto = {
        name: 'Sarah Chen',
        email: 'sarah.chen@example.com',
        role: 'user',
      };

      const savedUser = {
        id: '550e8400-e29b-41d4-a716-446655440000',
        ...createDto,
        createdAt: new Date(),
        updatedAt: new Date(),
      };

      mockRepository.create.mockReturnValue(savedUser);
      mockRepository.save.mockResolvedValue(savedUser);

      const result = await service.create(createDto);

      expect(result).toEqual(savedUser);
      expect(mockRepository.create).toHaveBeenCalledWith(createDto);
      expect(mockRepository.save).toHaveBeenCalled();
    });
  });

  describe('getUser', () => {
    it('should throw NOT_FOUND for non-existent user', async () => {
      mockRepository.findOne.mockResolvedValue(null);

      await expect(
        service.findOne('550e8400-e29b-41d4-a716-446655440000')
      ).rejects.toThrow(
        expect.objectContaining({
          error: expect.objectContaining({
            code: status.NOT_FOUND,
          }),
        })
      );
    });
  });
});

Verification

Test the generated service:

# Generate TypeScript from proto
npm run proto:generate

# Run tests
npm test

# Start microservice
npm run start:dev

You should see:

  • Proto compilation successful
  • All tests passing (green output)
  • Server listening on 0.0.0.0:50051

Test with grpcurl:

# Install grpcurl
brew install grpcurl  # macOS
# or download from https://github.com/fullstorydev/grpcurl

# List services
grpcurl -plaintext localhost:50051 list

# Create user
grpcurl -plaintext -d '{
  "name": "Sarah Chen",
  "email": "sarah@example.com",
  "role": "user"
}' localhost:50051 com.company.user.v1.UserService/CreateUser

Expected response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Sarah Chen",
  "email": "sarah@example.com",
  "role": "user",
  "createdAt": "2026-02-14T10:30:00Z",
  "updatedAt": "2026-02-14T10:30:00Z"
}

What You Learned

  • AI generates 80% of boilerplate when prompted with structure
  • Always add validation layer manually (AI skips it)
  • Use @grpc/grpc-js not deprecated grpc package
  • Proper error handling requires gRPC status codes
  • Testing setup should be part of initial generation

Limitations:

  • AI can't know your specific business logic
  • Generated tests need real edge cases added
  • Proto files may need adjustment for existing systems

Bonus: AI Prompts Cheatsheet

For proto files:

Create a proto file for [domain] with:
- Package: com.company.[domain].v1
- Services: [list operations]
- Follow Google API design guide
- Include pagination and timestamps

For NestJS services:

Create NestJS service implementing [proto service] with:
- TypeORM for PostgreSQL
- Error handling with gRPC status codes
- Transaction support
- Soft deletes

For testing:

Create Jest tests for [service] covering:
- Happy path with realistic data
- Validation errors
- gRPC error codes
- Edge cases: [list specific scenarios]

Time saved: 2-3 hours of boilerplate → 12 minutes of reviewing and adjusting.


Tested on NestJS 10.3.0, @grpc/grpc-js 1.10.0, Node.js 22.x, TypeScript 5.5