

Web APIのプロパティ名、camelCaseかsnake_caseかで悩んだ
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
Web APIのJSONプロパティ名をcamelCaseにするかsnake_caseにするか、社内で話が出たので整理した。単なる好みの問題だと思ってたけど、実際は「変換コストをどこに持たせるか」の設計判断だった。
生成側の観点
TypeScript / React / GraphQL / AdonisJSあたりが中心なら、camelCaseがそのままJSONにシリアライズできて楽。
type User = { userId: string; createdAt: string;};DBがsnake_caseならマッピング層は要るけど、DTOと JSON の形が一致するのでアプリコード側の見通しは良くなる。
逆にOpenAPI First・REST API・多言語クライアント前提ならsnake_caseの方が有利。SQLとの親和性が高いし、Go/Python/Rubyの慣習にも合う。
{ "user_id": "...", "created_at": "..."}Swift/Kotlinクライアント側の話
ここが一番効いてくるところだった。SwiftのAPI Design Guidelines(swift.org)はlowerCamelCaseを明記してる。なので、snake_caseなAPIを受ける場合はCodingKeysで変換が要る。
struct User: Decodable { let userId: String let createdAt: Date
enum CodingKeys: String, CodingKey { case userId = "user_id" case createdAt = "created_at" }}Kotlinも同様で、Gsonなら@SerializedNameで対応できる。
data class User( @SerializedName("user_id") val userId: String,
@SerializedName("created_at") val createdAt: Instant)ただしMoshiを使ってる場合はこの変換がそのまま効かないケースがあるらしく、KotlinJsonAdapterFactoryを別途噛ませる必要が出てくることがあるみたい(実機未確認)。ライブラリ次第で挙動が変わるので、そこは要注意。
SDK自動生成なら話は変わる
OpenAPI GeneratorやSwagger CodegenはmodelPropertyNamingオプションでcamelCase/snake_case/PascalCase/originalを切り替えられる。なのでSDKを完全自動生成する前提なら、API側がsnake_caseでもクライアント側の不利は小さくなる。
とはいえGitHubのIssueを見ると、Kotlin生成でquery paramが意図せずcamelCaseに変換されて実際のリクエストが壊れる、みたいな報告もちらほらある。「自動生成だから安心」と全面的に信頼するのはちょっと危ない。
結論
- TypeScript / React / Swift / Kotlinが主要クライアント → camelCaseが変換コスト最小で自然
- 外部公開API / OpenAPI First / 多言語クライアント対応重視 → snake_caseに合理性あり
- SDK完全自動生成前提なら、この差はある程度吸収される
うちの場合はTypeScript中心でSwift/Kotlinクライアントも持ってるので、DBはsnake_case、API(JSON)とアプリコードはcamelCaseで境界を一箇所に絞る方針に倒すのが良さそう。