You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 14:38:00