乐闻世界logo
搜索文章和话题

What are the methods for NestJS performance optimization?

2月21日 17:11

NestJS Performance Optimization Explained

Performance Optimization Overview

Performance optimization is key to building high-performance NestJS applications. Through proper architecture design, code optimization, and resource management, you can significantly improve application response speed and throughput.

1. Database Optimization

Query Optimization

Use Indexes

typescript
@Entity('users') export class User { @PrimaryGeneratedColumn() id: number; @Index() @Column() email: string; @Index() @Column() username: string; @Column() name: string; }

Avoid N+1 Queries

typescript
// Bad way - N+1 queries async getUsersWithOrders() { const users = await this.userRepository.find(); for (const user of users) { user.orders = await this.orderRepository.find({ where: { userId: user.id } }); } return users; } // Good way - Use JOIN async getUsersWithOrders() { return this.userRepository.find({ relations: ['orders'], }); }

Use Pagination

typescript
async findAll(page: number = 1, limit: number = 10) { const [data, total] = await this.userRepository.findAndCount({ skip: (page - 1) * limit, take: limit, }); return { data, total, page, totalPages: Math.ceil(total / limit), }; }

Connection Pool Configuration

typescript
TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'test', entities: [User], extra: { connectionLimit: 20, // Adjust based on server configuration }, })

2. Caching Strategies

Use Redis Cache

typescript
import { Injectable, Inject } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; @Injectable() export class UserService { constructor(@Inject(CACHE_MANAGER) private cacheManager: Cache) {} async findOne(id: number) { const cacheKey = `user_${id}`; const cachedUser = await this.cacheManager.get(cacheKey); if (cachedUser) { return cachedUser; } const user = await this.userRepository.findOne({ where: { id } }); await this.cacheManager.set(cacheKey, user, 3600); // Cache for 1 hour return user; } }

HTTP Caching

typescript
import { Controller, Get, Header } from '@nestjs/common'; @Controller('users') export class UsersController { @Get() @Header('Cache-Control', 'public, max-age=300') // Cache for 5 minutes findAll() { return this.usersService.findAll(); } }

Interceptor Caching

typescript
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'; import { Observable } from 'rxjs'; import { tap } from 'rxjs/operators'; @Injectable() export class CacheInterceptor implements NestInterceptor { private cache = new Map(); intercept(context: ExecutionContext, next: CallHandler): Observable<any> { const request = context.switchToHttp().getRequest(); const cacheKey = request.url; if (this.cache.has(cacheKey)) { return of(this.cache.get(cacheKey)); } return next.handle().pipe( tap(data => { this.cache.set(cacheKey, data); }), ); } }

3. Asynchronous Processing

Use Async/Await

typescript
// Good way async findAll() { return this.userRepository.find(); } // Bad way - Synchronous blocking findAll() { return this.userRepository.findSync(); }

Parallel Processing

typescript
// Bad way - Serial execution async getUserData(userId: number) { const user = await this.userRepository.findOne({ where: { id: userId } }); const orders = await this.orderRepository.find({ where: { userId } }); const notifications = await this.notificationRepository.find({ where: { userId } }); return { user, orders, notifications }; } // Good way - Parallel execution async getUserData(userId: number) { const [user, orders, notifications] = await Promise.all([ this.userRepository.findOne({ where: { id: userId } }), this.orderRepository.find({ where: { userId } }), this.notificationRepository.find({ where: { userId } }), ]); return { user, orders, notifications }; }

Use Queue for Time-Consuming Tasks

typescript
import { Injectable } from '@nestjs/common'; import { InjectQueue } from '@nestjs/bull'; import { Queue } from 'bull'; @Injectable() export class EmailService { constructor(@InjectQueue('email') private emailQueue: Queue) {} async sendEmail(to: string, subject: string, body: string) { await this.emailQueue.add('send-email', { to, subject, body }); } }

4. Compression

Enable Gzip Compression

typescript
import compression from 'compression'; async function bootstrap() { const app = await NestFactory.create(AppModule); app.use(compression()); await app.listen(3000); }

Configure Compression Level

typescript
app.use(compression({ level: 6, // Compression level 1-9 threshold: 1024, // Only compress responses larger than 1KB }));

5. Static Resource Optimization

Use CDN

typescript
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Configure static resources to use CDN app.useStaticAssets('public', { prefix: '/static/', cacheControl: true, maxAge: 31536000, // 1 year }); await app.listen(3000); }

Image Optimization

typescript
import { Controller, Get, Param, Res } from '@nestjs/common'; import { Response } from 'express'; import sharp from 'sharp'; @Controller('images') export class ImageController { @Get(':filename') async getImage(@Param('filename') filename: string, @Res() res: Response) { const image = sharp(`./public/images/${filename}`); // Optimize image based on request parameters const width = parseInt(req.query.width) || 800; const height = parseInt(req.query.height) || 600; image .resize(width, height) .jpeg({ quality: 80 }) .pipe(res); } }

6. Code Optimization

Use Lazy Loading Modules

typescript
@Module({ imports: [ // Lazy load modules UsersModule, OrdersModule, ], }) export class AppModule {}

Avoid Unnecessary Calculations

typescript
// Bad way async processUsers(users: User[]) { return users.map(user => { const expensiveResult = this.expensiveCalculation(user); return { ...user, result: expensiveResult }; }); } // Good way - Use caching async processUsers(users: User[]) { const cache = new Map(); return users.map(user => { const cacheKey = user.id; if (!cache.has(cacheKey)) { cache.set(cacheKey, this.expensiveCalculation(user)); } return { ...user, result: cache.get(cacheKey) }; }); }

Use Streams for Large Data

typescript
import { Controller, Get, StreamableFile } from '@nestjs/common'; import { createReadStream } from 'fs'; @Controller('files') export class FileController { @Get('download/:filename') downloadFile(@Param('filename') filename: string): StreamableFile { const file = createReadStream(`./files/${filename}`); return new StreamableFile(file); } }

7. Monitoring and Analysis

Performance Monitoring

typescript
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 now = Date.now(); const request = context.switchToHttp().getRequest(); return next.handle().pipe( tap(() => { const duration = Date.now() - now; this.logger.log( `${request.method} ${request.url} - ${duration}ms`, ); }), ); } }

Use APM Tools

typescript
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { Agent } from '@elastic/apm-node'; const apm = new Agent({ serviceName: 'nestjs-app', serverUrl: 'http://localhost:8200', }); async function bootstrap() { const app = await NestFactory.create(AppModule); await app.listen(3000); } bootstrap();

8. Load Balancing

Use PM2 Cluster Mode

bash
npm install pm2 -g pm2 start dist/main.js -i max --name nestjs-app

Configure PM2

javascript
// ecosystem.config.js module.exports = { apps: [{ name: 'nestjs-app', script: './dist/main.js', instances: 'max', exec_mode: 'cluster', env: { NODE_ENV: 'production', PORT: 3000, }, }], };

9. Memory Optimization

Avoid Memory Leaks

typescript
// Bad way - May cause memory leaks @Injectable() export class CacheService { private cache = new Map(); set(key: string, value: any) { this.cache.set(key, value); } } // Good way - Use TTL @Injectable() export class CacheService { private cache = new Map(); private ttl = 3600000; // 1 hour set(key: string, value: any) { this.cache.set(key, { value, expires: Date.now() + this.ttl, }); // Periodically clean up expired cache this.cleanup(); } private cleanup() { const now = Date.now(); for (const [key, data] of this.cache.entries()) { if (data.expires < now) { this.cache.delete(key); } } } }

Use Object Pool

typescript
@Injectable() export class ObjectPool<T> { private pool: T[] = []; private factory: () => T; constructor(factory: () => T, size: number = 10) { this.factory = factory; for (let i = 0; i < size; i++) { this.pool.push(factory()); } } acquire(): T { return this.pool.pop() || this.factory(); } release(obj: T): void { this.pool.push(obj); } }

10. Network Optimization

Use HTTP/2

typescript
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { NestExpressApplication } from '@nestjs/platform-express'; async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); await app.listen(3000); } bootstrap();

Configure Keep-Alive

typescript
async function bootstrap() { const app = await NestFactory.create(AppModule); const server = app.getHttpServer(); server.keepAliveTimeout = 65000; server.headersTimeout = 66000; await app.listen(3000); }

Performance Optimization Best Practices

  1. Monitor Performance: Continuously monitor application performance metrics
  2. Benchmarking: Establish performance baselines and test regularly
  3. Progressive Optimization: Optimize gradually, one aspect at a time
  4. Code Review: Regularly review code to discover performance issues
  5. Use Caching: Reasonably use caching to reduce database queries
  6. Asynchronous Processing: Use async and parallel processing to improve efficiency
  7. Resource Compression: Enable compression to reduce data transfer
  8. Load Balancing: Use load balancing to distribute request pressure
  9. Regular Cleanup: Regularly clean up cache and temporary data
  10. Documentation: Record optimization process and results

Summary

NestJS performance optimization provides:

  • Database query optimization
  • Multiple caching strategies
  • Asynchronous processing capabilities
  • Resource compression techniques
  • Monitoring and analysis tools

Mastering performance optimization is key to building high-performance NestJS applications. By properly applying various optimization techniques and best practices, you can significantly improve application performance, response speed, and user experience. Performance optimization is a continuous process that requires constant adjustment and improvement based on actual application scenarios.

标签:NestJS