Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why are null and undefined variables removed from params #1139

Closed
Billy- opened this issue Oct 18, 2017 · 59 comments · Fixed by #1987 · May be fixed by #5338
Closed

Why are null and undefined variables removed from params #1139

Billy- opened this issue Oct 18, 2017 · 59 comments · Fixed by #1987 · May be fixed by #5338

Comments

@Billy-
Copy link

Billy- commented Oct 18, 2017

if (val === null || typeof val === 'undefined') {

I would expect

let a
let b
axios({ params: { a, b })

To construct ?a=&b=

@ChrisLahaye
Copy link

ChrisLahaye commented Oct 18, 2017

Its the right behaviour, more strictly it should actually generate something as file?keythathasnullvalue without equal sign and value. It would be wrong to assume an empty string as value. In order to achieve your desired result you have to assign an empty string to it.

@Billy-
Copy link
Author

Billy- commented Oct 20, 2017

Without an equals to me should denote a boolean true. null and undefined are both "empty" values and so I would argue this is equivalent of an empty string.

The fact that a property exists on the query object to me means that you want something to be passed through. If you don't want something in your query string then surely you should not have that property on your params object in the first place?

@ChrisLahaye
Copy link

A variable without value is called a declaration which is of type undefined, which actually evaluates in false. If you would evaluate an empty string '' it also evaluates in false, but they are not equivalent since they differ in type, the prior is undefined, the latter is of type string.

I think I agree with you, but it would be wrong to assume what that something should be. Assuming an empty string would be wrong, but with {a: undefined} it should indeed more preferably generate path?a.

@PeterDKC
Copy link

Passing a key with no value as a URL parameter is a perfectly valid use case.

path?name=Bob&manager

is a valid URL. If no value is passed for the key, it should just leave off the =, but I agree that an empty string is different than null.

@ahtik
Copy link

ahtik commented Mar 31, 2018

The current behavior is actually in line with how JSON values are handled in JavaScript:
JSON.stringify({ a:1, b:undefined, c:2 }) produces {"a":1,"c":2}

{a: undefined} is often used as a construct to ignore a JSON key.
If you need "a=" then '' seems to be semantically good enough.

Maybe {a: null} should be interpreted as "?a" param? But not undefined.

@Radiergummi
Copy link

@ahtik 👍 for

Maybe {a: null} should be interpreted as "?a" param? But not undefined.

@abodelot
Copy link

abodelot commented Oct 1, 2018

What about a web APIs which expects a payload parameter to be explicitly set to null?
Some APIs enforce strict type checking rules and do not accept an empty string as a substitute for null.

In my opinion, axios should not remove parameters with null value from the payload.

@luponZ
Copy link

luponZ commented Oct 8, 2018

you can import qs and set paramsSerializer(params){return qs.stringify(params)} to replace default setting.

@ghost
Copy link

ghost commented Jan 25, 2019

Is the {a: null} to ?a conversion acceptable?
If so I'll try implementing this and creating a pull request.

@Radiergummi
Copy link

@abodelot So how do these APIs expect null values to be serialized? As ?foo=null? because in that case, setting { foo: 'null' } would be more appropriate than using actual null.

@ahtik
Copy link

ahtik commented Jan 25, 2019

@abodelot by payload and its null-handling, are we still talking about the URL parameters, not the POST/PATCH/etc payload?

I'd think {a: null} to ?a conversion is a rather safe solution, and both ?a=null and ?a='null' conversion would potentially cause confusion, requires special handling for the url parser, makes it impossible to send an actual "null" string value...

@ghost
Copy link

ghost commented Feb 1, 2019

Pull request is up. (#1987)
It was a pretty simple edit.

@abodelot
Copy link

abodelot commented Feb 1, 2019

@Radiergummi In a JSON payload, I'd expect an actual null value. When serialized in a query string, that would be ?key=.

I'm afraid using { key: 'null' } isn't a proper solution, value would still be a string.

@vuoriliikaluoma thank you for taking care of this issue

@ghost
Copy link

ghost commented Feb 1, 2019

All of these are possible:
{ key: undefined } to ? (not included)
{ key: null } to ?key
{ key: '' } to ?key=
{ key: 'null' } to ?key=null

@damusix
Copy link

damusix commented Aug 31, 2019

Why wouldn't you just allow anything to be send as the key? I think it's pretty ridiculous to remove false or null keys given the prevalence of JSON APIs. If I want to send { "name": "bob", "display_phone": false } it removes "display_phone". Unacceptable.

@Radiergummi
Copy link

Why wouldn't you just allow anything to be send as the key? I think it's pretty ridiculous to remove false or null keys given the prevalence of JSON APIs. If I want to send { "name": "bob", "display_phone": false } it removes "display_phone". Unacceptable.

You may be confusing query strings with body payload here. The object in question will be serialized into a query string. Using a literal "false" in a query string is seldomly seen as its pretty verbose, and needlessly so - a single "0" would suffice.
Now, the actual problem is that we need a way to submit empty query string keys like ?foo without an assigned value.
The PR by vuoriliikaluoma takes care of this and should be merged soon :)

@hussain
Copy link

hussain commented Jan 22, 2020

Actually axios interceptor can do the trick. I faced same scenario where json payload contained undefined or null values to be passed to an API as empty string.

I added an interceptor as below snippet. This also works for nested object ;) thanks recursion!

/**
 * Add json cleaner to set undefined/null values to empty string.
 * This is to prevent axios(json to string using jsonify) from removing those keys
 * when converting json paylod to string.
 */
function cleanJSON(json) {
    for (let key in json) {
        if (json[key] === undefined || json[key] === null) {
            json[key] = '';
        } else if (typeof json[key] === 'object')
            json[key] = cleanJSON(json[key]);
    }
    return json;
}
// Add a request interceptor
window.axios.interceptors.request.use(
    function(config) {
        if (['post', 'put', 'patch'].includes(config.method)) {
            config.data = cleanJSON(config.data);
        }
        return config;
    },
    function(error) {
        return Promise.reject(error);
    }
);

@Munawwar
Copy link

Munawwar commented Mar 9, 2020

I actually like the default behavior, since it's query string:

{ key: undefined } to ?
{ key: null } to ?
{ key: '' } to ?key=
{ key: 'null' } to ?key=null

From a strictly typed perspective, only string is a valid query string (see... it's a self validating statement 😄 )..
null and undefined are not strings, nor should axios assume a string representation for them out-of-the-box. Use paramsSerializer config function or interceptors for changing for your use-case.

@PeterDKC
Copy link

@Munawwar that's not how HTTP works. The first two are valid HTTP query parameters and should not be removed from the query string just because they have no value for the key.

@Munawwar
Copy link

HTTP spec does not dictate null and undefined serialization (the latter of which is a JS only thing). There is a way to achieve each of the url param representation.. let me inverse the example

To achieve:
?key= use { key: '' }
? use {}
?key=null use { key: 'null' }

@PeterDKC
Copy link

That's not the same thing, and we're not talking about serialization. ?myKey is valid, and not the same as ?myKey=

@Munawwar
Copy link

Oh didn't think of that one.. I'll keep quite now 🤐

so null should do that but undefined shouldn't yes?
?key use { key: null }

@PeterDKC
Copy link

No, it would generally be used to translate into a Boolean or a state of some kind. /?hasCats the user has cats vs / the user doesn't have cats. It's the same outcome as having that value always present and true or false but can save you length on your URL ( browsers don't allow infinite strong length ) and just prettify your URL. You would check the request for existence of the key rather than it's value. The point is, it's valid HTTP, but Axios doesn't account for it and simply black holes keys with no =. Thus still an open issue afaik.

@fredaas
Copy link

fredaas commented May 7, 2020

Just wasted 30 minutes of debugging to discover that axios deletes fields with empty strings ("") in the payload. If that's not a bug I don't know what is.

@mrjackyliang
Copy link

@fredaas I just tried adding an empty string into one of the keys in params. It works for me. I am using v0.19.2

@mrjackyliang
Copy link

That's not the same thing, and we're not talking about serialization. ?myKey is valid, and not the same as ?myKey=

@PeterDKC Very true. I think what axios is trying to do is balance the differences between an object vs params format.

If a key includes the = or doesn't, the URL works regardless (unless URI RFC mandates this requirement). Honestly, I do hate that danging = sign, but I have chosen to just accept the fact even if I don't like it. Took me quite a bit of time to accept it.

Is passing a query string parameter (whose value is null) without the equals sign valid?

@jawn-ha
Copy link

jawn-ha commented Feb 3, 2023

is there any progress?

@rcbevans
Copy link

Why is this still an issue.

Was able to work around by providing a default param serializer in the client code:

const defaultParamsSerializer = (
  params: Record<string, unknown>,
  options?: ParamsSerializerOptions,
  //@ts-ignore
) => new URLSearchParams(params).toString();

export const ApiClient = axios.create({
  baseURL: '/api',
  headers: {
    'Content-Type': 'application/json',
  },
  paramsSerializer: { serialize: defaultParamsSerializer },
  withCredentials: true,
});

@sergeushenecz
Copy link

is there any progress?

@mrjackyliang
Copy link

4 years after my last comment, and here me on this, I think it's fair to keep it as is.

I think it goes into the study of what defining "nothing" means, and what axios did here is actually correct.

Took me a long time to figure out why, but it has to do with the understanding of what "void" really means.

@PeterDKC
Copy link

@mrjackyliang Hard disagree. Keys with no values are and always have been a valid part of the HTTP spec. This intervenes and breaks otherwise valid functionally. That's incorrect behavior and always has been.

@mrjackyliang
Copy link

mrjackyliang commented Nov 25, 2023

@mrjackyliang Hard disagree. Keys with no values are and always have been a valid part of the HTTP spec. This intervenes and breaks otherwise valid functionally. That's incorrect behavior and always has been.

In what way? Can you give an example of your use case?

Under HTTP spec, keys with no values are valid, but if you're trying to convert null or undefined under the JS perspective as a way to represent an empty string under the HTTP spec, where is the connection in that?

However, nobody is stopping you from putting an empty string as a value, in which, is correct from how I see it, and HTTP params are of string type.

@sergeushenecz
Copy link

@mrjackyliang Sometimes need to pass param=null it means on api that filter by this property which in database null. If you don’t pass then not needed filter this field by null.

@mrjackyliang
Copy link

mrjackyliang commented Nov 26, 2023

@mrjackyliang Sometimes need to pass param=null it means on api that filter by this property which in database null. If you don’t pass then not needed filter this field by null.

If that's the case, and you're passing null of string type, this can be parsed using JSON.parse.

But, if you're up for some fun, using Zod to parse web requests is actually very neat.

@rcbevans
Copy link

@mrjackyliang Sometimes need to pass param=null it means on api that filter by this property which in database null. If you don’t pass then not needed filter this field by null.

If that's the case, and you're passing null of string type, this can be parsed using JSON.parse.

But, if you're up for some fun, using Zod to parse web requests is actually very neat.

Null is stripped out by axios and isn't sent as a query parameter for the server to parse; that's the point of this issue.

@sergeushenecz
Copy link

sergeushenecz commented Nov 28, 2023

@rcbevans If i right understood. He said to pass null as string "null"

@mrjackyliang
Copy link

mrjackyliang commented Nov 28, 2023

@mrjackyliang Sometimes need to pass param=null it means on api that filter by this property which in database null. If you don’t pass then not needed filter this field by null.

If that's the case, and you're passing null of string type, this can be parsed using JSON.parse.

But, if you're up for some fun, using Zod to parse web requests is actually very neat.

Null is stripped out by axios and isn't sent as a query parameter for the server to parse; that's the point of this issue.

@rcbevans Yes, then you should convert the null of object type to a null of string type, and have the receiving end do the reverse.

How does it make sense to send null of object type over HTTP when only HTTP requests understand are strings and JavaScript understands a null of string type isn't a null of object type?

When you send JSON over HTTP, you're not really sending actual objects. That's why there is Content-Type headers specified to make the receiving end understand that it's JSON data and not plain text.

Yes, typeof null === 'object', and well typeof undefined === 'undefined', both which are NOT of string type

Also, I know many developers like to do things in a shortcut manner (I do too, that's why I initially disliked it), but this isn't about how fast you can implement code, it's how accurate and fast you can implement code, which in itself is a contradiction.

@PeterDKC
Copy link

@mrjackyliang Hard disagree. Keys with no values are and always have been a valid part of the HTTP spec. This intervenes and breaks otherwise valid functionally. That's incorrect behavior and always has been.

In what way? Can you give an example of your use case?

Under HTTP spec, keys with no values are valid, but if you're trying to convert null or undefined under the JS perspective as a way to represent an empty string under the HTTP spec, where is the connection in that?

However, nobody is stopping you from putting an empty string as a value, in which, is correct from how I see it, and HTTP params are of string type.

?pets=6&hasCats

^ That's a valid HTTP param line that's been accepted by servers all over the Internet since always. The hasCats key should come through to the server bag untouched. This has nothing to do with null. It's about Axios inserting its own opinion about parameter bags that should otherwise be valid in any other context. Again, this is incorrect behavior that does not adhere to the HTTP spec.

Axios is proactively taking an action (removing the key from the parameter bag) that the developer doesn't intend and the server would not have an issue with. That's what we're talking about and always have been talking about.

@mrjackyliang
Copy link

@mrjackyliang Hard disagree. Keys with no values are and always have been a valid part of the HTTP spec. This intervenes and breaks otherwise valid functionally. That's incorrect behavior and always has been.

In what way? Can you give an example of your use case?

Under HTTP spec, keys with no values are valid, but if you're trying to convert null or undefined under the JS perspective as a way to represent an empty string under the HTTP spec, where is the connection in that?

However, nobody is stopping you from putting an empty string as a value, in which, is correct from how I see it, and HTTP params are of string type.

?pets=6&hasCats

^ That's a valid HTTP param line that's been accepted by servers all over the Internet since always. The hasCats key should come through to the server bag untouched. This has nothing to do with null. It's about Axios inserting its own opinion about parameter bags that should otherwise be valid in any other context. Again, this is incorrect behavior that does not adhere to the HTTP spec.

Axios is proactively taking an action (removing the key from the parameter bag) that the developer doesn't intend and the server would not have an issue with. That's what we're talking about and always have been talking about.

I don't think it's Axios inserting an opinion. It's a problem with 2 solutions that don't agree with each other.

@ergonomicus
Copy link

After all these years it should have been implemented as an option that anyone can switch on or off.

@mrjackyliang
Copy link

After all these years it should have been implemented as an option that anyone can switch on or off.

I mean, nobody's stopping anyone from creating a workaround as a package.

@ergonomicus
Copy link

ergonomicus commented Nov 29, 2023

I mean, nobody's stopping anyone from creating a workaround as a package.

I am already using a workaround, still, I think that it should be an option you can just turn on if you prefer.

@mrjackyliang
Copy link

I mean, nobody's stopping anyone from creating a workaround as a package.

I am already using a workaround, still, I think that it should be an option you can just turn on if you prefer.

I mean, who doesn't? But I do believe this has to do more with economics.

Why cover everything in one product when you can have other developers extend functionality for you? Just the same reason why axios doesn't have native cookie jar support, but request (depreciated) does.

SSH8560 added a commit to murramge/GongGam_APP_Project that referenced this issue Apr 24, 2024
axios params에서 undefined는 자동으로 제외됨
{ key: undefined } to ? (not included)
{ key: null } to ?key
{ key: '' } to ?key=
{ key: 'null' } to ?key=null

axios/axios#1139
@julichan
Copy link

julichan commented Jul 28, 2024

Hello, i just stumbled on this page when looking for axios behavior with undefined values and I don't understand some propositions made here so i'm adding my two cents without meaning to add oil on the fire.
undefined is a value introduced as a mistake to javascript by its creator as it should have been null from the beginning. Then null was added later to further make the language confusing by another dev. They were kept as is for compatibility reasons.
To avoid complicating this, it is widely known that undefined and null should not be treated differently anywhere so why should axios do so? Beside have you seen any other language that have two values like null and undefined?
Also i hear the end of this confusion is highly wanted and i read that microsoft is recommending to always use undefined and avoid null. There is also a typescript linter rule to prohibit usage of null values i believe.
The support for flags without values (no equal) should be treated as boolean (not a null nor undefined) so perhaps add an option to omit false and change true into a flag would be a valid idea.
In the case of body parameter, i don't know how else it would be treated than a boolean...

The question of shortening an url doesn't feel like it's the responsability of axios to me. You pass parameters, you get them serialized.

Also i did not understand the idea of "null" instead of null, that would make deserialization on backend side even more confusing, would it not? A string is a string, not a transport method and moreover, there exists languages where null is written as nil and perhaps there are some other words as well. I think omitting the value is a good choice for compatibility reasons. I guess it would be possible to add an option to set a wanted value when the passed parameter is null though.

I do agree that empty strings should be passed but i don't see much their use aside from a user forgetting to fill a field 🤔

Here again no oil on the fire. Just my two cents.

@PeterDKC
Copy link

@julichan This has nothing to do with values in JS. It has everything to do with how urls are interpreted on the server layer & the HTTP spec.

http://foo.co/?cats=2&hasFleas <-- That is a valid URl, but Axios explicitly turns this into http://foo.co/?cats=2. This is contrary to the spec and incorrect behavior.

Again, nothing to do with null vs undefined. Nothing to do with Microsoft. Nothing to do with TypeScript. It's an explicitly incorrect behavioral choice on the part of the Axios library that breaks specific application full stack behavior under certain conditions.

@julichan
Copy link

@PeterDKC , i do understand this, just added my two cents and for the info i called url?value a flag. Your point doesn't give me an explanation on why this should be changed so i still stand with my point and agree to disagree with you through i'm not one to make any decision here. Look at the big picture, not just the http spec. 🤣

@PeterDKC
Copy link

Your point doesn't give me an explanation on why this should be changed

If you read my explanation and don't understand why it should be changed then maybe you should read it a few more times because I literally stated what it breaks and why it needs to be changed, and it has nothing to do with either of your two cents.

@julichan
Copy link

Relax @PeterDKC, this is not a war...

@PeterDKC
Copy link

@julichan Yes, you're right. It's a very simple straightforward issue that has been explained ad nauseum for years in this thread, and it should have been resolved years ago.

@jasonsaayman
Copy link
Member

if someone would like to open a PR please do so, but for now I'm closing this its super old

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet