NestJS完全マスターガイド2025 - Node.jsエンタープライズ開発の決定版
NestJS 11.1.3の最新機能からエンタープライズでの実装パターンまで徹底解説。パフォーマンス比較、マイクロサービス構築、実際の導入事例を通じて、Node.jsでのスケーラブルな開発手法を完全網羅します。
TypeScriptファーストのNode.jsフレームワークNestJSを徹底解説。v11系の新機能、マイクロサービス構築、GraphQL統合、本番環境での運用まで、エンタープライズ開発に必要な全てを網羅します。
NestJS は、スケーラブルな Node.jsサーバーサイドアプリケーションを構築するためのプログレッシブなフレームワークです。TypeScriptを第一級市民として扱い、Angular 風のアーキテクチャを採用することで、エンタープライズレベルの開発をサポートします。
標準的なアーキテクチャの欠如
コードの構造化・保守性の課題
エンタープライズ向けの構造化されたフレームワーク
71,000+ GitHubスター、大手企業での採用拡大
2025 年 6 月時点の最新バージョンは v11.1.3 です。v11 系では以下の重要な改善が行われています:
機能 | v10 | v11 | 改善内容 |
---|---|---|---|
パフォーマンス | 基準 | +35% | ルーティング最適化 |
起動時間 | 基準 | -40% | 遅延初期化の改善 |
メモリ使用量 | 基準 | -25% | DI コンテナの最適化 |
TypeScript | 4.x | 5.x | 最新の型機能サポート |
Node.js | 16+ | 18+ | ネイティブFetch API対応 |
チャートを読み込み中...
# Nest CLIのインストール
npm i -g @nestjs/cli
# 新規プロジェクトの作成
nest new my-nestjs-app
# プロジェクトディレクトリへ移動
cd my-nestjs-app
# 開発サーバーの起動
npm run start:dev
NestJS の強力な DI コンテナは、疎結合で保守性の高いコードを実現します:
// users.service.ts
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private usersRepository: Repository<User>,
private configService: ConfigService,
@Inject(CACHE_MANAGER)
private cacheManager: Cache,
) {}
async findAll(query: PaginationDto): Promise<User[]> {
const cacheKey = `users_${JSON.stringify(query)}`;
const cached = await this.cacheManager.get<User[]>(cacheKey);
if (cached) {
return cached;
}
const users = await this.usersRepository.find({
take: query.limit,
skip: query.offset,
order: { createdAt: 'DESC' }
});
await this.cacheManager.set(cacheKey, users, 300);
return users;
}
}
// DTOでのバリデーション
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator';
export class CreateUserDto {
@IsNotEmpty({ message: '名前は必須です' })
@MinLength(2, { message: '名前は2文字以上必要です' })
name: string;
@IsEmail({}, { message: '有効なメールアドレスを入力してください' })
email: string;
@IsNotEmpty()
@MinLength(8, { message: 'パスワードは8文字以上必要です' })
password: string;
}
// コントローラーでの使用
@Post()
@UsePipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}))
async create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
// グローバル例外フィルター
@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
constructor(
private readonly httpAdapterHost: HttpAdapterHost,
private readonly logger: Logger,
) {}
catch(exception: unknown, host: ArgumentsHost): void {
const { httpAdapter } = this.httpAdapterHost;
const ctx = host.switchToHttp();
const httpStatus =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
const responseBody = {
statusCode: httpStatus,
timestamp: new Date().toISOString(),
path: httpAdapter.getRequestUrl(ctx.getRequest()),
message: exception instanceof HttpException
? exception.message
: 'Internal server error',
};
this.logger.error(
`HTTP Status: ${httpStatus} Error: ${JSON.stringify(responseBody)}`,
exception instanceof Error ? exception.stack : '',
);
httpAdapter.reply(ctx.getResponse(), responseBody, httpStatus);
}
}
// カスタムパイプの実装
@Injectable()
export class ParseIntPipe implements PipeTransform<string, number> {
transform(value: string, metadata: ArgumentMetadata): number {
const val = parseInt(value, 10);
if (isNaN(val)) {
throw new BadRequestException(`${metadata.data}は数値である必要があります`);
}
return val;
}
}
// 使用例
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.usersService.findOne(id);
}
// より高度なパイプ
@Injectable()
export class FileSizeValidationPipe implements PipeTransform {
transform(value: Express.Multer.File) {
const maxSize = 5 * 1024 * 1024; // 5MB
if (value.size > maxSize) {
throw new PayloadTooLargeException('ファイルサイズは5MB以下にしてください');
}
return value;
}
}
NestJS は、マイクロサービスの構築を簡単にします:
// マイクロサービスの作成
async function bootstrap() {
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
AppModule,
{
transport: Transport.REDIS,
options: {
host: 'localhost',
port: 6379,
},
},
);
await app.listen();
}
// パターンベースのメッセージハンドリング
@Controller()
export class MathController {
@MessagePattern({ cmd: 'sum' })
accumulate(data: number[]): number {
return data.reduce((a, b) => a + b, 0);
}
@EventPattern('user_created')
async handleUserCreated(data: UserCreatedEvent) {
// イベント処理
await this.emailService.sendWelcomeEmail(data.email);
}
}
// GraphQLモジュールの設定
@Module({
imports: [
GraphQLModule.forRoot<ApolloDriverConfig>({
driver: ApolloDriver,
autoSchemaFile: true,
playground: process.env.NODE_ENV !== 'production',
subscriptions: {
'graphql-ws': true,
},
}),
],
})
export class AppModule {}
// Resolverの実装
@Resolver(() => User)
export class UsersResolver {
constructor(private usersService: UsersService) {}
@Query(() => [User])
async users(
@Args('limit', { type: () => Int, defaultValue: 10 }) limit: number,
@Args('offset', { type: () => Int, defaultValue: 0 }) offset: number,
): Promise<User[]> {
return this.usersService.findAll({ limit, offset });
}
@Mutation(() => User)
async createUser(
@Args('createUserInput') createUserInput: CreateUserInput,
): Promise<User> {
return this.usersService.create(createUserInput);
}
@Subscription(() => User)
userAdded() {
return pubSub.asyncIterator('userAdded');
}
}
リアルタイム通信も簡単に実装できます:
@WebSocketGateway({
cors: {
origin: process.env.CLIENT_URL,
credentials: true,
},
})
export class ChatGateway implements OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer()
server: Server;
private logger: Logger = new Logger('ChatGateway');
@SubscribeMessage('sendMessage')
async handleMessage(
@MessageBody() message: string,
@ConnectedSocket() client: Socket,
): Promise<void> {
const user = await this.getUserFromSocket(client);
this.server.emit('newMessage', {
user: user.name,
message,
timestamp: new Date(),
});
}
handleConnection(client: Socket) {
this.logger.log(`Client connected: ${client.id}`);
}
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
}
}
// 遅延読み込みモジュール
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
},
];
// NestJSでの実装
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
load: [databaseConfig, redisConfig],
cache: true, // 設定のキャッシュ
}),
],
})
export class AppModule {}
// Redis キャッシュの設定
@Module({
imports: [
CacheModule.register({
store: redisStore,
host: 'localhost',
port: 6379,
ttl: 300, // 5分
}),
],
})
export class AppModule {}
// インターセプターでのキャッシュ
@Injectable()
export class HttpCacheInterceptor extends CacheInterceptor {
trackBy(context: ExecutionContext): string | undefined {
const request = context.switchToHttp().getRequest();
const { httpAdapter } = this.httpAdapterHost;
const isGetRequest = httpAdapter.getRequestMethod(request) === 'GET';
const excludePaths = ['/api/auth', '/api/admin'];
if (!isGetRequest || excludePaths.some(path =>
httpAdapter.getRequestUrl(request).includes(path)
)) {
return undefined;
}
return httpAdapter.getRequestUrl(request);
}
}
テストタイプ | 対象 | ツール | 実行時間 |
---|---|---|---|
単体テスト | Service/Controller | Jest | 〜1秒 |
統合テスト | API エンドポイント | Supertest | 〜5秒 |
E2Eテスト | ユーザーフロー | Cypress/Playwright | 〜30秒 |
負荷テスト | パフォーマンス | k6/Artillery | 〜5分 |
// サービスの単体テスト
describe('UsersService', () => {
let service: UsersService;
let repository: Repository<User>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{
provide: getRepositoryToken(User),
useClass: Repository,
},
],
}).compile();
service = module.get<UsersService>(UsersService);
repository = module.get<Repository<User>>(getRepositoryToken(User));
});
it('should create a user', async () => {
const createUserDto: CreateUserDto = {
name: 'Test User',
email: 'test@example.com',
password: 'password123',
};
jest.spyOn(repository, 'save').mockResolvedValue({
id: 1,
...createUserDto,
} as User);
const result = await service.create(createUserDto);
expect(result).toEqual({
id: 1,
...createUserDto,
});
});
});
@Controller('health')
export class HealthController {
constructor(
private health: HealthCheckService,
private db: TypeOrmHealthIndicator,
private redis: RedisHealthIndicator,
) {}
@Get()
@HealthCheck()
check() {
return this.health.check([
() => this.db.pingCheck('database'),
() => this.redis.pingCheck('redis'),
() => this.checkDiskSpace(),
() => this.checkMemoryUsage(),
]);
}
private async checkDiskSpace() {
const stats = await checkDiskSpace('/');
const isHealthy = stats.free > 1024 * 1024 * 1024; // 1GB
return {
disk: {
status: isHealthy ? 'up' : 'down',
free: stats.free,
},
};
}
}
// セキュリティミドルウェアの設定
app.use(helmet());
app.use(
rateLimit({
windowMs: 15 * 60 * 1000, // 15分
max: 100, // リクエスト数の上限
}),
);
app.enableCors({
origin: process.env.ALLOWED_ORIGINS?.split(','),
credentials: true,
});
NestJS は、エンタープライズレベルの Node.jsアプリケーションを構築する上で、最も包括的で構造化されたフレームワークです。Azure Functions との統合においても優れたパフォーマンスを発揮しています。
NestJS は、Node.jsでエンタープライズグレードのアプリケーションを構築するための最適なフレームワークです。TypeScriptファーストの設計、強力な DI コンテナ、豊富なエコシステムにより、保守性と拡張性の高いアプリケーションを効率的に開発できます。
2025 年現在、v11 系では大幅なパフォーマンス改善が実現され、マイクロサービス、GraphQL、WebSocket など、モダンなアーキテクチャパターンへの対応も充実しています。