NestJS module for automatic request/response casing transformation (snake_case ↔ camelCase)
npm install @royzhang107/nest-casingA NestJS module for automatic request/response casing transformation between snake_case and camelCase.
Write your DTOs in camelCase while your API communicates in snake_case — automatically.
- 🔄 Bidirectional transformation — Incoming snake_case → camelCase, outgoing camelCase → snake_case
- 🌐 Global module — Apply once, works everywhere
- 🎯 Flexible — Use globally or per-route with interceptors/pipes
- 📦 Pure TypeScript — Zero external dependencies for casing logic
- 🔧 Configurable — Optional ValidationPipe integration
- ⚡ Dual ESM/CJS — Works in any module system
``bash`
npm install @royzhang107/nest-casing
Import CasingModule in your root module:
`typescript
import { Module } from '@nestjs/common';
import { CasingModule } from '@royzhang107/nest-casing';
@Module({
imports: [
CasingModule.forRoot(),
],
})
export class AppModule {}
`
That's it! Your API now:
- Accepts snake_case request bodies and transforms them to camelCasecamelCase
- Returns responses with keys transformed to snake_case
`typescript
CasingModule.forRoot({
// Default: 'snake-to-camel'
// - 'snake-to-camel': Input snake_case → camelCase, Output camelCase → snake_case
// - 'camel-to-snake': Input camelCase → snake_case, Output snake_case → camelCase
direction: 'snake-to-camel',
// Enable built-in ValidationPipe
enableValidation: true,
validationOptions: {
transform: true,
whitelist: true,
forbidNonWhitelisted: false,
},
})
`
Use forRootAsync when options depend on other providers:
`typescript
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { CasingModule } from '@royzhang107/nest-casing';
@Module({
imports: [
ConfigModule.forRoot(),
CasingModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
direction: config.get('CASING_DIRECTION') || 'snake-to-camel',
enableValidation: true,
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
`
Use interceptors and pipes individually when you need fine-grained control:
`typescript
import { Controller, Post, Body, UseInterceptors, UsePipes } from '@nestjs/common';
import {
CamelToSnakeInterceptor,
SnakeToCamelPipe,
} from '@royzhang107/nest-casing';
@Controller('users')
export class UsersController {
@Post()
@UseInterceptors(CamelToSnakeInterceptor)
@UsePipes(SnakeToCamelPipe)
create(@Body() createUserDto: CreateUserDto) {
// createUserDto has camelCase keys
return this.usersService.create(createUserDto);
// Response will have snake_case keys
}
}
`
The casing utilities are also exported for direct use:
`typescript
import {
toCamelCase,
toSnakeCase,
keysToCamel,
keysToSnake,
} from '@royzhang107/nest-casing';
// String transformation
toCamelCase('user_name'); // 'userName'
toSnakeCase('userName'); // 'user_name'
// Deep object key transformation
keysToCamel({ user_name: 'John', user_age: 30 });
// { userName: 'John', userAge: 30 }
keysToSnake({ userName: 'John', userAge: 30 });
// { user_name: 'John', user_age: 30 }
`
| Function | Description |
|----------|-------------|
| toCamelCase(str) | Convert string to camelCase |toSnakeCase(str)
| | Convert string to snake_case |toPascalCase(str)
| | Convert string to PascalCase |toKebabCase(str)
| | Convert string to kebab-case |keysToCamel(obj)
| | Deep transform object keys to camelCase |keysToSnake(obj)
| | Deep transform object keys to snake_case |keysToPascal(obj)
| | Deep transform object keys to PascalCase |keysToKebab(obj)
| | Deep transform object keys to kebab-case |transformKeys(obj, fn)
| | Deep transform object keys with custom function |
In your Nx workspace, import from the library path:
`typescript`
// In apps/your-app/src/app/app.module.ts
import { CasingModule } from '@royzhang107/nest-casing';
`bashBuild the library
npx nx build @royzhang/nest-casing
$3
The workspace already has the path mapping in
tsconfig.base.json:`json
{
"compilerOptions": {
"paths": {
"@royzhang107/nest-casing": ["libs/nestjs-casing/src/index.ts"]
}
}
}
`API Reference
$3
| Method | Description |
|--------|-------------|
|
forRoot(options?) | Register module with static configuration |
| forRootAsync(options) | Register module with async configuration |$3
| Class | Description |
|-------|-------------|
|
CamelToSnakeInterceptor | Transform response keys from camelCase to snake_case |
| SnakeToCamelInterceptor | Transform response keys from snake_case to camelCase |$3
| Class | Description |
|-------|-------------|
|
SnakeToCamelPipe | Transform request body keys from snake_case to camelCase |
| CamelToSnakePipe | Transform request body keys from camelCase to snake_case |Example Flow
`
Request Body (from client):
{
"user_name": "john_doe",
"email_address": "john@example.com"
}
↓
SnakeToCamelPipe transforms to:
{
"userName": "john_doe",
"emailAddress": "john@example.com"
}
↓
Your controller/service works with camelCase
↓
CamelToSnakeInterceptor transforms response:
{
"user_name": "john_doe",
"email_address": "john@example.com",
"created_at": "2024-01-01T00:00:00Z"
}
``MIT