NestJS如何优雅实现JSON蛇形与驼峰命名自动互转?
我之前刚解决过一模一样的需求!不用手动写DTO赋值,也不用改前端请求格式,直接通过NestJS的全局管道和拦截器就能搞定全局的snake_case ↔ camelCase自动转换,完美适配MikroORM的实体类。给你一套完整的实现方案:
核心思路
通过两个全局组件实现双向转换:
- ValidationPipe:拦截入站请求,把
snake_case的请求体自动转成camelCase,直接映射到你的实体类/DTO字段 - ClassSerializerInterceptor:拦截出站响应,把
camelCase的实体类实例自动转成snake_case返回给前端
步骤1:配置全局ValidationPipe(请求体转camelCase)
在main.ts里配置全局ValidationPipe,开启自动转换,并加入下划线转驼峰的逻辑。我们可以直接用class-transformer内置的工具函数,不用自己写正则:
import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import { underscoreToCamelCase } from 'class-transformer'; // 递归处理嵌套对象的下划线转驼峰 function convertToCamelCase(obj: any): any { if (typeof obj !== 'object' || obj === null) return obj; if (Array.isArray(obj)) return obj.map(convertToCamelCase); const transformed: Record<string, any> = {}; for (const key in obj) { transformed[underscoreToCamelCase(key)] = convertToCamelCase(obj[key]); } return transformed; } async function bootstrap() { const app = await NestFactory.create(AppModule); // 全局请求体转换:snake_case → camelCase app.useGlobalPipes( new ValidationPipe({ transform: true, // 开启自动转换请求体到类实例 enableImplicitConversion: true, // 自动转换基本类型 transformOptions: { excludeExtraneousValues: true, // 只保留类中定义的字段 // 自定义转换逻辑 transformer: (value) => convertToCamelCase(value), }, }), ); // 后面还要加拦截器,继续往下看 await app.listen(3000); } bootstrap();
步骤2:配置全局ClassSerializerInterceptor(响应体转snake_case)
同样在main.ts里,添加全局序列化拦截器,处理响应体的驼峰转下划线:
// 先导入需要的模块 import { ClassSerializerInterceptor } from '@nestjs/common'; import { camelCaseToUnderscore } from 'class-transformer'; // 递归处理嵌套对象的驼峰转下划线 function convertToSnakeCase(obj: any): any { if (typeof obj !== 'object' || obj === null) return obj; if (Array.isArray(obj)) return obj.map(convertToSnakeCase); const transformed: Record<string, any> = {}; for (const key in obj) { transformed[camelCaseToUnderscore(key)] = convertToSnakeCase(obj[key]); } return transformed; } // 在bootstrap函数里添加: app.useGlobalInterceptors( new ClassSerializerInterceptor(app.get('Reflector'), { excludeExtraneousValues: true, // 只返回类中暴露的字段 transformer: (value) => convertToSnakeCase(value), }), );
步骤3:适配MikroORM命名策略
确保你的MikroORM配置中,实体的camelCase字段对应数据库的snake_case列,这样从数据库查询出来的结果会自动转成camelCase,再经过拦截器转成snake_case返回。
在mikro-orm.config.ts里配置命名策略:
import { MikroOrmModuleOptions } from '@mikro-orm/nestjs'; import { camelCaseToUnderscore } from 'class-transformer'; export const mikroOrmConfig: MikroOrmModuleOptions = { entities: ['./dist/**/*.entity.js'], entitiesTs: ['./src/**/*.entity.ts'], dbName: 'your-db-name', type: 'postgresql', // 换成你用的数据库类型 namingStrategy: { // 实体字段(camelCase)转数据库列名(snake_case) propertyToColumnName: (prop) => camelCaseToUnderscore(prop), }, };
验证效果
现在你的控制器可以直接接收实体类参数,不用任何额外处理:
import { Controller, Post, Body } from '@nestjs/common'; import { User } from './user.entity'; import { UserRepository } from './user.repository'; @Controller('users') export class UserController { constructor(private readonly userRepo: UserRepository) {} @Post() async create(@Body() user: User) { // 这里的user已经是camelCase字段,直接传入MikroORM仓库方法 await this.userRepo.persistAndFlush(user); // 返回的user会自动转成snake_case响应给前端 return user; } }
比如前端发送:
{ "user_name": "john_doe", "email_address": "john@example.com" }
控制器里的user对象会是:
{ userName: "john_doe", emailAddress: "john@example.com" }
响应给前端的JSON会是:
{ "id": 1, "user_name": "john_doe", "email_address": "john@example.com" }
完全符合你的需求,不用手动写任何转换逻辑!
内容的提问来源于stack exchange,提问作者Dave Nathanael
相关产品推荐
相关产品推荐

