API-Naming-Workflow
API Request & Response Case Converter
Behalte camelCase innerhalb deiner Anwendung und übersetze snake_case-JSON an der Request-/Response-Grenze. Teste beide Richtungen und kopiere ein Fetch- oder Axios-Muster.
Response: snake_case → camelCase · Request: camelCase → snake_case
API-Grenzrichtung
{
"userProfile": {
"firstName": "Ada",
"createdAt": "2026-09-12"
}
}API-Grenzcode erzeugen
Wähle ein JSON-HTTP-Client-Muster. Das Beispiel konvertiert ausgehende App-Objekte in snake_case und eingehende JSON-Antworten in camelCase.
type JsonRecord = Record<string, unknown>
function isJsonObject(value: unknown): value is JsonRecord {
if (value === null || typeof value !== "object" || Array.isArray(value)) {
return false
}
return Object.getPrototypeOf(value) === Object.prototype
}
function splitPrefix(key: string) {
const prefix = key.match(/^[_$]+/u)?.[0] ?? ""
return { prefix, value: key.slice(prefix.length) }
}
function splitWords(value: string) {
return value
.replace(/([A-Z]+)([A-Z][a-z])/gu, "$1 $2")
.replace(/([a-z0-9])([A-Z])/gu, "$1 $2")
.split(/[^A-Za-z0-9]+/u)
.filter(Boolean)
}
function toCamelKey(key: string) {
const { prefix, value } = splitPrefix(key)
const words = splitWords(value).map((word) => word.toLowerCase())
const [firstWord, ...restWords] = words
if (!firstWord) return key
return prefix + firstWord + restWords
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join("")
}
function toSnakeKey(key: string) {
const { prefix, value } = splitPrefix(key)
const words = splitWords(value).map((word) => word.toLowerCase())
return words.length ? prefix + words.join("_") : key
}
function mapJsonKeys(
value: unknown,
convertKey: (key: string) => string,
): unknown {
if (Array.isArray(value)) {
return value.map((item) => mapJsonKeys(item, convertKey))
}
if (!isJsonObject(value)) {
return value
}
const output: JsonRecord = {}
for (const [key, child] of Object.entries(value)) {
const nextKey = convertKey(key)
if (Object.hasOwn(output, nextKey)) {
throw new Error(`Key collision after case conversion: ${nextKey}`)
}
output[nextKey] = mapJsonKeys(child, convertKey)
}
return output
}
export function keysToCamel(value: unknown) {
return mapJsonKeys(value, toCamelKey)
}
export function keysToSnake(value: unknown) {
return mapJsonKeys(value, toSnakeKey)
}
export async function apiJson<T>(
url: string,
options: { method?: string; body?: unknown } = {},
): Promise<T> {
const response = await fetch(url, {
method: options.method ?? (options.body === undefined ? "GET" : "POST"),
headers: { "content-type": "application/json" },
body:
options.body === undefined
? undefined
: JSON.stringify(keysToSnake(options.body)),
})
if (!response.ok) {
throw new Error(`API request failed: ${response.status}`)
}
const wireData: unknown = await response.json()
return keysToCamel(wireData) as T
}Die erzeugte Grenze ist für JSON-Payloads gedacht. FormData, Blob, Streams, Dateien und andere Nicht-JSON-Bodys bleiben außerhalb des Mappers.
Namenskonvertierung an der API-Grenze bündeln
Komponenten, Hooks, Stores und Domain-Code behalten eine interne Konvention, während der externe JSON-Vertrag eine andere nutzen kann.
1. Eingehende Antworten normalisieren
Nach dem Parsen des Server-JSON werden snake_case-Schlüssel rekursiv in camelCase umgewandelt.
2. Ausgehende Requests normalisieren
Vor der JSON-Serialisierung werden camelCase-Schlüssel der App in das von der API erwartete snake_case umgewandelt.
3. Nicht-JSON-Bodys außerhalb lassen
FormData, Blob, Streams, Dateien oder beliebige Klasseninstanzen nicht durch einen JSON-Key-Mapper schicken.
Stop Paths, Regex-Ausschlüsse, Diff oder lokale JSON-Dateien nötig?
Nutze den JSON Key Case Converter für erweiterte Objektregeln und übertrage dieselbe Grenzlogik anschließend in den Anwendungscode.
JSON Key Case Converter öffnenAPI-Workflow-Implementierungen
Wähle den passenden Guide für HTTP-Client, Query-Funktion, Serializer oder Model-Alias-Schicht.
Fetch API
JSON-Requests und -Responses kapseln, ohne Nicht-JSON-Bodys zu verändern.
Axios
Dedizierte Axios-Instanz mit Request-/Response-Interceptors.
TanStack Query
Case-Mapping in gemeinsamen Query-/Mutation-Funktionen statt in Komponenten.
Jackson
Naming-Strategien an Java-Serialisierungsgrenzen verwenden.
Pydantic
Model-Aliase trennen Python-Feldnamen von externem JSON.
Python-Dictionaries
dict/list-Schlüssel rekursiv konvertieren, wenn keine Model-Schicht vorhanden ist.