@mpyw, pronounced as "mappy." Web Backend Developer/Engineer. Laravel Contributor.
More introduced in mpyw/laravel-packages.
More introduced in Repositories.
Axios transformer/interceptor that converts snake_case/camelCase
License: MIT License
@mpyw, pronounced as "mappy." Web Backend Developer/Engineer. Laravel Contributor.
More introduced in mpyw/laravel-packages.
More introduced in Repositories.
Is it possible to add type safety to the transformations by making use of the recent TypeScript feature that appeared to have been added exactly for this very exact use case? I figured this was the perfect feature for this module.
https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/#key-remapping-mapped-types
constant variables are something like this : I_AM_A_CONSTANT
thank you.
Hi,
Nice work you did!
So there's two options to preserve key names, string[]
and Function
.
I'm currently using the string[] approach, but it might be useful to only apply the middleware only on a subset of the request/response data.
Eg in a Json api there's (very often) extra metadata included in the response:
const requestData = {
data : {
data: {
name: 'name',
billing_street: 'street name',
...
},
meta: {
current_page: 1,
from: 1,
...
},
...
}
}
Can we use the Functions-approach here so that the casing middleware is only applied on the data.data
attribute?
Thanks!
Hello! Can you convert error response to camelCase too. Thank you.
We can never polyfill FormData.prototype.entries()
in the current version of React Native (v0.54). We should display warning instead of throwing error.
After upgrade to new version. I getting error. My value is moment js date.
I want to run middleware only for some urls, how can I do it now? I don't really want a separate instance for it, so I'm applying middleware to default object.
Maybe it's a good idea, if you pass your inner functions to caseMiddleware
, as a second argument, so we can decide, under which conditions, we'd run it?
options?.caseMiddleware?.responseTransformer ||
createCamelResponseTransformer(options),
];
axios.interceptors.request.use(
options?.caseMiddleware?.requestInterceptor ||
createSnakeParamsInterceptor(options)
);
to
(config) => options?.caseMiddleware?.responseTransformer(config, createCamelResponseTransformer(options)) ||
createCamelResponseTransformer(options),
];
axios.interceptors.request.use(
(config) => options?.caseMiddleware?.requestInterceptor(config, createSnakeParamsInterceptor(options)) ||
createSnakeParamsInterceptor(options)
);
but in some more cleaner manner
Axios v1.0.0 got released a few days ago (see https://github.com/axios/axios/releases/tag/v1.0.0) but axios-case-converter
only works with versions between >=0.23.0 and <1.0.0 unfortunately.
Thank you for creating this package, I found it very useful.
Hi, nice to found this awesome library.
I try to use this in my project, but I got the library not covert camelCase to snake_case when sending data with JSON.stringify on FormData.
OS: macOS Mojave
Browser: Google Chrome Version 83.0.4103.116 (Official Build) (64-bit)
And here my code:
// helper: api.js
const API = applyCaseMiddleware(
Axios.create({
baseURL: 'http://mysite.com/api/v1/',
headers: {
'X-Requested-With': 'xmlhttprequest'
},
})
);
export default API;
// main.js
const formData = new FormData();
formData.append('document', null);
formData.append('keterangan', this.form.keterangan);
formData.append('approvers', JSON.stringify(approvers));
API.post('/api/v1/document/store', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
}).then(res => {
// success
}).catch(err => {
// something went wrong
});
And when checking the browser console, I got this:
The data attributes still in camelCase.
How can I fix this?
Looks like when a uuid is received in a request as a key, funny stuff happens sometimes.
This is what is expected of it:
000_000_aaa_000 => 000-000-aaa-000
However, it sometimes does stuff like:
000_000_aaa_000 => 000-000-aAa-000
000_000_aaa_000 => 000-000aaa000
axios@v1
support is requested in #45 and the corresponding pull-request #46 is submitted by @siketyan. However, we have identified the bug that mutated response headers unexpectedly discard their changes.
For now we'll merge #46, but at this stage it is recommended to be still kept on axios@v0
. axios@v1
may not be production-ready yet.
EDIT: I made a mistake, this can be closed (I can't find the delete button)
Is this possible?
{
"region": {
"ch-de": "CH German Part"
}
}
ends up like chDe: CH German Part
applyConverters
affects unexpectedly axios global object. axios.defaults.transformRequest
and axios.defaults.transformResponse
should be immutable.
Related: instance.defaults changes global object · Issue #385 · axios/axios
The library is perfect.
I would prefer to see headers untouched. Is there any options to disable HTTP headers processing completely, disable it once, and don't worry about keeping preservedKeys up to date.
Thanks.
class ClientImpl implements Client {
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
this.client = applyCaseMiddleware(axios.create());
}
readonly baseUrl: string;
readonly client: axios.AxiosInstance;
...
When I use
axios.get('/bla', {
params: {
foo__bar: 1234
}
})
the generated url is /bla?foo_bar
instead of /bla?foo__bar
When upgrading to axios v0.23.0 on one of my packages I got these npm warnings:
$ npm i [email protected]
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: <my-package>@<my-version>
npm WARN Found: [email protected]
npm WARN node_modules/axios
npm WARN peer axios@"^0.21.1" from [email protected]
npm WARN node_modules/axios-case-converter
npm WARN axios-case-converter@"^0.8.1" from the root project
npm WARN 1 more (the root project)
npm WARN
npm WARN Could not resolve dependency:
npm WARN peer axios@"^0.21.1" from [email protected]
npm WARN node_modules/axios-case-converter
npm WARN axios-case-converter@"^0.8.1" from the root project
Maybe it would be better to be less strict in peerDependencies
and replace it with something like this:
"peerDependencies": {
"axios": ">=0.21.1 <1.0.0"
}
to avoid these warnings?
Axios is currently as 0.20, should this package also update the version to 0.20?
I'm sorry that I'm not willing to implement these features now. I'll explain some reasons. See also the following implementation for reference.
export const applyCaseMiddleware: ApplyCaseMiddleware = (axios, options?) => {
axios.defaults.transformRequest = [
options?.caseMiddleware?.requestTransformer ||
createSnakeRequestTransformer(options),
...(Array.isArray(axios.defaults.transformRequest)
? axios.defaults.transformRequest
: axios.defaults.transformRequest !== undefined
? [axios.defaults.transformRequest]
: []),
];
axios.defaults.transformResponse = [
...(Array.isArray(axios.defaults.transformResponse)
? axios.defaults.transformResponse
: axios.defaults.transformResponse !== undefined
? [axios.defaults.transformResponse]
: []),
options?.caseMiddleware?.responseTransformer ||
createCamelResponseTransformer(options),
];
axios.interceptors.request.use(
options?.caseMiddleware?.requestInterceptor ||
createSnakeParamsInterceptor(options)
);
return axios;
};
According to the Official Documentation, you can manually apply interceptor like this:
axios.interceptors.request.use(
options?.caseMiddleware?.requestInterceptor ||
createSnakeParamsInterceptor(options),
null,
{ runWhen(config) { /* ... */ } }
);
With this feature, it is possible to implement it on the library side. But I'm not willing to do so because it is difficult to achieve the same functionality on Transformer
and it also looks unstable.
The implementation in Transformer
corresponding to the Interceptor
checking runWhen()
is the following part.
It looks quite difficult to determine the URL in this layer since request config
is not given as an argument here.
The current best practice is:
Axios
instances with your own wrapper function.Axios
instance to use by the the argument condition.Argument of type 'import("xx/node_modules/axios/index").AxiosInstance' is not assignable to parameter of type 'import("xx/node_modules/@types/axios-case-converter/node_modules/axios/index").AxiosInstance'.
The types of 'defaults.adapter' are incompatible between these types.
Type 'import("xx/node_modules/axios/index").AxiosAdapter | undefined' is not assignable to type 'import("xx/node_modules/@types/axios-case-converter/node_modules/axios/index").AxiosAdapter | undefined'.
Type 'import("xx/node_modules/axios/index").AxiosAdapter' is not assignable to type 'import("xx/node_modules/@types/axios-case-converter/node_modules/axios/index").AxiosAdapter'.
Types of parameters 'config' and 'config' are incompatible.
Type 'import("xx/node_modules/@types/axios-case-converter/node_modules/axios/index").AxiosRequestConfig' is not assignable to type 'import("xx/node_modules/axios/index").AxiosRequestConfig'.
Types of property 'method' are incompatible.
Type 'string | undefined' is not assignable to type '"get" | "GET" | "delete" | "DELETE" | "head" | "HEAD" | "options" | "OPTIONS" | "post" | "POST" | "put" | "PUT" | "patch" | "PATCH" | "link" | "LINK" | "unlink" | "UNLINK" | undefined'.
Type 'string' is not assignable to type '"get" | "GET" | "delete" | "DELETE" | "head" | "HEAD" | "options" | "OPTIONS" | "post" | "POST" | "put" | "PUT" | "patch" | "PATCH" | "link" | "LINK" | "unlink" | "UNLINK" | undefined'.ts(2345)
They are so confusing:
We can rename them if we allow breaking changes.
I'll start this task after #22 is merged.
It would be nice to have an options that help you to exempt certain fields from being converted.
For example, we have a field _destroy
received from the backend, but after camelCasing it converts to destroy
. There is no possibility to send back the field to the API's as _destroy
,since _.snakeCase(_destroy
) or _.snakeCase(destroy
) returns the value as destroy
It will be good if we can have something likethis:
// applyConverters(<axiosInstance>, [<converterOptions>] )
applyConverters(axios.create(), { exception: ['_destroy'] } )
In RN throws iterate exception [object ArrayIterator is not iterable] where response is array of objects like
[{ field: 'foo', message: 'bar' }]
I think this problem around "for...of" operator for array and its polyfill. This bug isnt reproducible for usualy browser enviroment, only for RN. Can you fix it?
Hi! 👋
Firstly, thanks for your work on this project! 🙂
I wanted to bring to your attention an issue I encountered while working on a project that involves updating axios
from version 0.24.0
to 1.x
and axios-case-converter
from version 0.9.0
to 1.1.0
.
After the update, I observed a problem related to response headers case conversion. After some debugging, I narrowed down the issue to the overwriteHeadersOrNoop
function, particularly the validation within it:
if (
options?.ignoreHeaders ||
(!isPlainObject(headers) && !isAxiosHeaders(headers))
)
Upon investigation, it seems that the problem arises when the project is deployed, involving bundling, minification, and obfuscation. The isAxiosHeaders(headers)
validation fails, even though the headers are, in fact, of type AxiosHeaders
. This occurs because the validation relies on checking the constructor name.
Is there a reason for the use of the constructor name instead of validating whether it is an instance of AxiosHeaders
?
To confirm this behavior, I made a patch to the [email protected]
and added some log statements to the code:
import { AxiosHeaders } from "axios";
...
const overwriteHeadersOrNoop = (
...
): void => {
console.log(
`isPlainObject: ${isPlainObject(headers)},
isAxiosHeaders: ${isAxiosHeaders(headers)},
instanceof: ${headers instanceof AxiosHeaders}
`);
}
When running the code locally, the logs showed the expected results. However, after deployment, the logs indicated that isAxiosHeaders(headers)
was returning false
, even though headers instanceof AxiosHeaders
was true
.
It appears that the issue might be related to the obfuscation process affecting the constructor name comparison.
For additional context, here is the configuration for the Axios instance and the relevant request:
Axios Instance:
const axiosInstance = applyConverters(
axios.create({
baseURL: SOME_URL,
timeout: 10000,
paramsSerializer: (params) => stringify(params, { arrayFormat: 'repeat' }),
})
);
Request:
axiosInstance.post(SOME_URL, formData, {
headers: {
'Content-Type': 'multipart/form-data',
},
});
On Success:
const something = data.headers.contentLocation; // Fails due to incorrect case conversion
If you need any more details or if there's anything else I can do to assist in the resolution process, please let me know.
Thank you for your time and attention to this matter.
I switched from camelize
to this project because this allowed configuration.
I know issues are not for how tos but maybe you have a quick answer...
How to not change case? BMX
is being converted into bmx
I was recently hunting down a bug in an application where object keys appeared to be missing (although I could see them in the API response) only to later find out that they were (of course) converted to "camelcase". So
445ebf68-bd0e-44c6-b6b8-a70d9ba08115
became 445ebf68Bd0e44c6B6b8A70d9ba08115
.
Would be nice to have an option like { preserveUuid: true }
to not convert UUIDs (or possibly other formats) to camelCase. If you're interested, I could build that and create a PR.
Another, more flexible option would be something like a preservePattern: RegExp
so a user could specify which keys should not be converted but I think preserveUuid
would be fine for the beginning 😉
Ping me if you're interested!
Getting TypeError: applyCaseMiddleware is not a function
when using in Node 16.x with type module enabled.
import axios from 'axios';
import applyCaseMiddleware from 'axios-case-converter';
const axiosInstance = applyCaseMiddleware(axios.create());
I'm facing a scenario where we need to keep the camel case on the request but it's desirable to work as expected on the respective response (by converting the snake-case to camelCase).
On top of that this should happen only for a specific endpoint/url.
Is there a way of configure this for a set url?
Thanks in advanced!
Cheers,
Federico
Hello,
The readme is unclear to me :
How do I configure that in the options?
main code:
camel: (input, options) => {
return (input.charAt(0).toLowerCase() + input.slice(1)).replace(/[-_](.)/g, (match, group1) => group1.toUpperCase());
}
=>
input : pub_pub_256
return: pubPub256
however, when deployed with axios, it will return pubPub_256, instead of pubPub256.
Current work around is prepadding a char before numeric.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.