camelCase vs snake_case trong NestJS và Prisma | Đổi tên hàng loạt an toàn

(Cập nhật: 19 tháng 7, 2026 ) camelCase snake_case NestJS Prisma đặt tên biến API outsourcing
Kết luận

Xung đột camelCase / snake_case trong dự án Nest + Prisma gần như chắc chắn xuất hiện khi outsource FE/BE hoặc inherit DB cũ. Giữ DB = snake_case, TypeScript + JSON API = camelCase, nối bằng @map — đừng “đổi hết cho đồng”. Đổi tên hàng loạt chỉ trên danh sách identifier đã duyệt, không find-replace mù cả repo.

Ở Việt Nam, nhiều team product nhỏ thuê freelancer FE React và BE Nest riêng, hoặc nhận codebase từ client Nhật/Hàn với PostgreSQL đặt cột created_at, user_id. Một sprint sau, ticket bắt đầu xuất hiện: FE đọc userName nhưng API trả user_name, hoặc Prisma model viết user_name khiến ESLint/React nhìn “lạ”.

Bài này đi thẳng vào xung đột convention khi outsource, cách map Nest/Prisma đúng tầng, và quy trình bulk rename an toàn — kèm Case Converter khi cần sinh hàng loạt tên field.

Bốn case hay gặp (nhớ để đọc code người khác)

Quy ước đặt tên phổ biến
Kiểu Ví dụ Hay dùng ở đâu
camelCase userName, createdAt Biến/hàm JS·TS, JSON API Nest, field Prisma trong TS
snake_case user_name, created_at Cột SQL, file Python, một số API đối tác, env key
PascalCase UserProfile, CreateUserDto Class, React component, enum type
kebab-case user-profile, created-at URL path, CSS class, slug

Không có kiểu nào “sai tuyệt đối” — sai là trộn không có lớp chuyển đổi.


Góc NestJS + Prisma: conflict điển hình

Triệu chứng

// FE expect
user.userName

// Response thật
{ "user_name": "An", "created_at": "2026-07-01T00:00:00.000Z" }

Hoặc ngược lại: DB migration cũ là snake, junior viết Prisma:

model User {
  userName String // tạo cột "userName" — lệch chuẩn SQL team
}

Cách tách tầng (khuyến nghị)

model User {
  id        String   @id @default(cuid())
  userName  String   @map("user_name")
  createdAt DateTime @default(now()) @map("created_at")

  @@map("users")
}
  • Trong code TS: user.userName, user.createdAt
  • Trong DB: user_name, created_at
  • JSON API: serialize camelCase (mặc định class-transformer / Nest thường giữ tên property TS)

Client mobile hoặc đối tác outsource chỉ cần đọc OpenAPI — một convention cho JSON.

Khi API phải trả snake_case

Một số client doanh nghiệp (ERP, đối tác ngân hàng) bắt body snake. Khi đó:

  • Giữ Prisma/TS camel + @map
  • Thêm interceptor/DTO riêng cho outbound snake
  • Không đổi schema DB chỉ vì một consumer

Document rõ: internal = camel, partner X = snake.


Outsourcing: checklist tránh “đặt tên lệch sprint”

  1. Spec trước code: sheet hoặc OpenAPI liệt kê field JSON (camel) và cột DB (snake) cạnh nhau
  2. Một người duyệt acronym: userId vs userID, httpStatus vs HTTPStatus — chọn một, khóa trong lint nếu được
  3. Cấm FE tự đổi key: nếu BE sai, sửa BE hoặc BFF, không data.user_name || data.userName lan mọi nơi
  4. PR template: “Field mới đã có @map chưa?”
  5. Smoke test: một file fixtures/user.json khớp contract

Freelancer mới join project VN thường copy response Postman vào type bằng tay → dễ gõ created_At. Dùng converter để sinh từ danh sách cột DB sẽ ít lỗi hơn.


Bulk rename an toàn

Khi nào nên bulk convert

  • Migrate danh sách 30+ cột từ Excel spec → Prisma fields
  • Đổi convention JSON public chưa ra production
  • Sinh song song snake cho SQL và camel cho DTO

Khi nào không

  • Repo đã có production API + mobile cũ
  • Key đến từ hệ thống ngoài (access_token, error_code cố định)
  • Tên file, biến môi trường, message queue topic

Quy trình 6 bước

  1. Export danh sách identifier (một cột, mỗi dòng một tên)
  2. Dán vào Case Converter → chọn đích (camel / snake / Pascal / kebab)
  3. Review tay 5–10 tên đặc biệt (ID, URL, HTTP, Việt Nam không dấu trong identifier)
  4. Apply bằng rename symbol của IDE / codemod — không Notepad replace toàn repo
  5. Chạy test + so JSON fixture
  6. Commit tách: chore: align user DTO naming — dễ revert

Ví dụ chuyển nhanh:

user_name      → userName
created_at     → createdAt
is_active      → isActive
oauth_client   → oauthClient   (review: giữ OAuth?)

Tool hỗ trợ bốn dạng chính; xử lý trên trình duyệt — phù hợp khi không muốn dán schema nội bộ lên dịch vụ lạ.


Case study: team 2 BE + 1 FE freelance

Bối cảnh: Startup fintech nhỏ tại HCM, BE Nest + Prisma, FE Next. DB kế thừa từ prototype Python (toàn snake). FE viết type tay theo Postman lúc BE còn trả snake. Sau khi BE bật camel serializer, form “Cập nhật hồ sơ” lưu undefined vì đọc sai key.

Cách gỡ trong 1 ngày:

  1. Freeze contract: mọi endpoint v1 trả camel
  2. Sinh lại types từ OpenAPI (hoặc bảng map từ case converter)
  3. Thêm test e2e assert createdAt tồn tại, created_at không
  4. Partner báo cáo (vẫn cần snake) đi qua BFF /partner/v1 riêng

Không cần rename cột DB — chỉ sửa lớp API và FE.


Lỗi hay gặp khi convert

LỗiVí dụCách tránh
Đôi underscoreuser__nameuserName lệch kỳ vọngNormalize input trước
Chữ sốiso8601_dateReview thủ công
Acronymuser_iduserId (thường đúng) vs html_urlhtmlUrlStyle guide
Đổi cả string nội dungReplace user_name trong câu log tiếng AnhChỉ rename symbol
Trộn kebab trong JSON"user-name":JSON key nên camel hoặc snake, tránh kebab

Gợi ý convention một trang cho README

## Naming
- PostgreSQL columns: snake_case
- Prisma fields & Nest DTO: camelCase + @map
- REST JSON (public): camelCase
- URL path: kebab-case
- React components: PascalCase

Dán vào onboarding freelancer — giảm 50% comment “đổi tên giúp em” trên PR.


Liên kết hữu ích

Tóm lại: Nest/Prisma không buộc bạn chọn một case cho mọi tầng. Map rõ DB ↔ TS ↔ JSON, bulk rename có danh sách và review acronym, và dùng converter khi sinh hàng loạt — đó là cách sống sót khi outsource mà không phá production.