JavaScript · TypeScript · API 邊界
Axios snake_case ↔ camelCase
把命名轉換集中在單一 HTTP 邊界,不要讓 property rename 分散在整個 frontend。
建立 JSON 安全的遞迴 key mapper
function isPlainObject(value) {
return Object.prototype.toString.call(value) === "[object Object]"
}
function toCamelKey(key) { return key.replace(/_+([a-zA-Z0-9])/g, (_, char) => char.toUpperCase()) }
function toSnakeKey(key) { return key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase() }
function mapKeys(value, keyMapper) {
if (Array.isArray(value)) return value.map((item) => mapKeys(item, keyMapper))
if (!isPlainObject(value)) return value
const output = {}
for (const [key, child] of Object.entries(value)) {
const nextKey = keyMapper(key)
if (Object.hasOwn(output, nextKey)) throw new Error(`Key collision: ${nextKey}`)
output[nextKey] = mapKeys(child, keyMapper)
}
return output
}只轉換 plain JSON object 與 array,保留 scalar/non-plain value,並偵測 collision。
在專用 Axios instance 加上 request/response interceptor
import axios from "axios"
const api = axios.create({ baseURL: "/api" })
api.interceptors.request.use((config) => {
if (config.data && isPlainObject(config.data)) config.data = mapKeys(config.data, toSnakeKey)
if (config.params && isPlainObject(config.params)) config.params = mapKeys(config.params, toSnakeKey)
return config
})
api.interceptors.response.use((response) => {
response.data = mapKeys(response.data, toCamelKey)
return response
})將 request data/params 正規化為 snake_case,response.data 正規化為 camelCase。
application code 維持 camelCase
const response = await api.post("/users", {
displayName: "Ada Lovelace",
profileSettings: { emailNotifications: true },
})
console.log(response.data.createdAt)adapter 集中於 Axios instance 後,UI 與 TypeScript model 可以維持單一內部慣例。
正式環境注意事項
- 不要把 FormData、Blob、ArrayBuffer、stream、class instance 當作 JSON 遞迴轉換。
- 對不需轉換的 endpoint 使用專用 instance 或 opt-out。
- header 不要送進同一個 object-key mapper。
- TypeScript type 不會改 runtime key,interceptor 必須真的轉換資料。
線上測試 JSON 邊界
在實作前測試巢狀物件、陣列、排除鍵、stop paths、語意前綴、衝突與 key-only diff。
拖放 .json 檔案,或從裝置選取
檔案只會在瀏覽器中讀取。最大檔案大小:5 MB。
轉換後 JSON
{
"userProfile": {
"firstName": "Ada",
"createdAt": "2026-09-12"
}
}解析、檔案讀取、格式化與鍵名轉換都在瀏覽器本機執行。JSON 不會傳送到轉換伺服器。