Multiple fields with same key in query params (axios request)? - javascript

So the backend (not under my control) requires a query string like this:
http://example.com/?foo=5&foo=2&foo=11
But axios uses a JS object to send the request params:
axios.get('http://example.com/', { foo: 5 });
And obviously an object can't have multiple fields with the same key.
How can I send a request with multiple fields with the same key?

From the Request Config section of the axios documentation:
// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
params: {
ID: 12345
},
To use this in a request, you would do
const request = {
params: {
foo: [5, 2, 11]
}
}
axios.get('http://example.com/', request);
The issue with using a plain object is that array parameters are added as
http://example.com/?foo[]=5&foo[]=2&foo[]=11
To make a request to a URL without the [], use URLSearchParams
var params = new URLSearchParams();
params.append("foo", 5);
params.append("foo", 2);
params.append("foo", 11);
var request = {
params: params
};
axios.get('http://example.com/', request);
This will result in a request to
http://example.com/?foo=5&foo=2&foo=11

In Axios request config, you can override params serialization and then use QS NPM module to serialize array with repeat mode
let params = { foo: [5, 2] }
axios.get('path/to/api/',{params}) // URL : https://path/to/api?foo[]=5&foo[]=2
let myAxios = axios.create({
paramsSerializer: params => Qs.stringify(params, {arrayFormat: 'repeat'})
})
myAxios.get('path/to/api/',{params}) // URL : https://path/to/api?foo=5&foo=2

The correct answer with the latest axios version (in the year 2022) would be to set indexes: null in order to get arrayFormat: 'repeat'.
More info: https://github.com/axios/axios/issues/5058#issuecomment-1272107602
Example:
const {data} = await axios.get('https://postman-echo.com/get', {
params: {
a: ['b', 'c', 'd']
},
paramsSerializer: {
indexes: null // by default: false
}
});
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) ==> config.paramsSerializer.indexes = true // 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) ==> config.paramsSerializer.indexes = false// 'a[]=b&a[]=c' // Default
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) ==> config.paramsSerializer.indexes = null// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' }) ==> not supported // 'a=b,c'

Adding more details to #nhydock accepted answer.
When you do
var request = {foo: [5, 2, 11] }
axios.get('http://example.com/', request);
For django application you can receive these as
self.request.query_params.getlist('foo')
also.

Update: Axios already supports this out of the box as of axios#1.1.2
https://github.com/axios/axios/issues/5058#issuecomment-1272107602
#RNR1 Axios already supports this out of the box. By default, Axios encodes arrays in "bracket" format but supports 3 qs formats except "comma".
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' }) ==> config.paramsSerializer.indexes = true // 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' }) ==> config.paramsSerializer.indexes = false// 'a[]=b&a[]=c' // **Default**
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' }) ==> config.paramsSerializer.indexes = null// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' }) ==> **not supported** // 'a=b,c'
So to encode in arrayFormat: 'repeat' you need to do the following:
const {data} = await axios.get('https://postman-echo.com/get', {
params: {
a: ['b', 'c', 'd']
},
paramsSerializer: {
indexes: null // by default: false
}
});
Echo response:
{
args: { a: [ 'b', 'c', 'd' ] },
headers: {
'x-forwarded-proto': 'https',
'x-forwarded-port': '443',
host: 'postman-echo.com',
'x-amzn-trace-id': 'Root=1-63409c06-5d9fc0344ceaf9715466e0e3',
accept: 'application/json, text/plain, */*',
'user-agent': 'axios/1.1.0',
'accept-encoding': 'gzip, deflate, br'
},
url: 'https://postman-echo.com/get?a=b&a=c&a=d'
}

Disclaimer: I have never used Axios and couldn't find any reference in
the documentation. It is still worth a try. Personally, I would
implement it this way in a library.
It might also be possible to pass arrays as values for the parameter:
axios.get('http://example.com/', { foo: [1, 2, 3, 4, 5] });

If one uses the ready URLSearchParams the handling of multiple parameter values with the same name works with axios as well ... I guess the support for IE came in 2017 ... Works on Safari too, although the links claims that it might not ..
function getUrlParams(){
// handles multiple param values with the same name
var url_params = new URLSearchParams();
if( window.location.toString().indexOf("?") != -1) {
window.location.search.split('?')[1].split('#')[0]
.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
var attr = decodeURIComponent(key)
var val = decodeURIComponent(value)
url_params.append(attr,val);
});
} else {
// create a default set of params - you might not need this one ...
url_params = { some_param:"some_value" };
}
return url_params ;
}
function getBackEndData(url_params, back_end_url){
// the replace is just a fancy way of converting front-end to back-end call
return axios.get( back_end_url , { params: url_params } )
.then(response => {
return response.data ;
})
.catch(function(error) {
return error.response ;
console.log(error.response.data);
})
}

Related

Compare response in jest unit test case

I'm new to jest unit test case scenario, I have a scenario where in the response from the service that I called is of the below format
Artifact {
name: 'detection-v1.zip',
file_path: 'artifact\\bn-ds-anomalydetection-v1.zip',
is_tenant: false,
metadata: [
Registerfact {
name: 'ad',
_meta: [Object],
line_meta: [Object]
},
Registerfact {
name: 'ad-generic',
_meta: [Object],
line_meta: [Object]
}
]
}
how can i compare the above response in the jest service , I was trying to create a object but the Artifact name before the object is confusing how should i proceed
The test case is
test('test processArtifact method', async()=>{
const mockGetRestClient = jest.fn();
try{
const response = await factService.processfact(artifact)
console.log("response---",response)
// expect(response).toEqual()
}
catch(e){ }
})
I know its silly question ,but i'm confused hence posted it.
How should i create the static object to be put in .toEqual() ?
You can declare a global/static var with your response object on top of file. Or better declare it in some constants file and import here.
For Comparison:
Usually, if you have a simple object, you can use JSON.stringify. However, it may give error due to different order of object keys.
You should use assert for the deep comparison. There is method assert.deepEqual() which does deep comparison of objects.
an example for using assert from official docs
import assert from 'node:assert';
const obj1 = {
a: {
b: 1
}
};
const obj2 = {
a: {
b: 2
}
};
const obj3 = {
a: {
b: 1
}
};
const obj4 = Object.create(obj1);
assert.deepEqual(obj1, obj1);
// OK
// Values of b are different:
assert.deepEqual(obj1, obj2);
// AssertionError: { a: { b: 1 } } deepEqual { a: { b: 2 } }
assert.deepEqual(obj1, obj3);
// OK
// Prototypes are ignored:
assert.deepEqual(obj1, obj4);
// AssertionError: { a: { b: 1 } } deepEqual {}
Hope this helps, let me know if you have any questions.
You can use JSON.stringify in order to convert your object into a string and then compare this result to the one you expect.
console.log(JSON.stringify({
name: 'detection-v1.zip',
file_path: 'artifact\\bn-ds-anomalydetection-v1.zip',
is_tenant: false,
metadata: [
{Registerfact: {
name: 'ad',
_meta: {},
line_meta: {}
}},
{Registerfact: {
name: 'ad-generic',
_meta: {},
line_meta: {}
}}
]
}));

Is there a way to serializeParams in axios without relying on a 3rd party library such as qs?

When using the qs library we control how arrays in query parameters would be with option arrayFormat (quote from qs library docs below):
You may use the arrayFormat option to specify the format of the output array:
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'indices' })
// 'a[0]=b&a[1]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'brackets' })
// 'a[]=b&a[]=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'repeat' })
// 'a=b&a=c'
qs.stringify({ a: ['b', 'c'] }, { arrayFormat: 'comma' })
// 'a=b,c'
Is there a way to define this default array format in axios without having to import the qs library in my project?
You can use paramsSerializer.
For more information, you can see response and comments at https://stackoverflow.com/a/51444749.

Axios use duplicate params typescript

I'm working with an existing backend which I can not adjust anymore. I need to do a call which gets all messages. One of the params is type, this can be used to get all messages from a specific type. The backend developer had told me I had to use this multiple times looking like this:
/api/message?type=test&type=test2
So i tried sending it as a array, which resulted in:
/api/message?type[]=test&type[]=test2
Finding some information on SO made me use QS but this also doesn't give me the result I need
api/message?type%5B0%5D=test&type%5B1%5D=test2
export const getMessages = () =>
baseApi
.get('/message', {
params: {
type: [
'test',
'test2'
]
},
paramsSerializer: (params) => {
return qs.stringify(params)
}
})
.then((r) => r.data as Message[])
This isn't related to Axios. The qs library is responsible for generating that string. See its documentation:
When arrays are stringified, by default they are given explicit
indices:
qs.stringify({ a: ['b', 'c', 'd'] });
// 'a[0]=b&a[1]=c&a[2]=d'
You may override this by setting the indices option to false:
qs.stringify({ a: ['b', 'c', 'd'] }, { indices: false });
// 'a=b&a=c&a=d'
You can use the append method. Like (URLSearchParams):
const params = new URLSearchParams()
params.append("type","test")
params.append("type","test2")
Then, you set the params in the request just like you did:
export const getMessages = () =>
baseApi
.get('/message', {
params: {
params
},
paramsSerializer: (params) => {
return qs.stringify(params)
}
})
.then((r) => r.data as Message[])

Is it possible to apply passThrough() within a mock reply using axios-mock-adapter?

Environment:
NodeJS 8.1.2
axios 0.16.2
axios-mock-adapter 1.9.0
Testing a JSON-RPC endpoint, am I able to do something like this:
const mockHttpClient = new MockAdapter(axios, { delayResponse: 50 })
mockHttpClient.onPost().reply((config) => { // Capture all POST methods
const dataObj = JSON.parse(config.data) // Example data: '{"jsonrpc":"2.0","method":"getProduct","params":[123],"id":0}'
if (dataObj.method === 'getProduct') { // Recognised method, provide mock response override
return [200, { jsonrpc: '2.0', id: 0, result: { productId: 123, productName: 'Lorem' } }]
}
// TODO: PassThrough for all non-recognised methods
})
mockHttpClient.onAny().passThrough() // Allow pass through on anything that's not a POST method
You can do that by passing the call to the original adapter within the reply callback :
mockHttpClient.onPost().reply((config) => { // Capture all POST methods
const dataObj = JSON.parse(config.data) // Example data: '{"jsonrpc":"2.0","method":"getProduct","params":[123],"id":0}'
if (dataObj.method === 'getProduct') { // Recognised method, provide mock response override
return [200, { jsonrpc: '2.0', id: 0, result: { productId: 123, productName: 'Lorem' } }]
}
// PassThrough for all non-recognised methods
return mockHttpClient.originalAdapter(config);
})
It's essentially what passThrough() does.

Converting array of objects into array of strings

I'm working on an app where I need to pass an array of strings to a backend service something like
const ids = [];
for (let i = 0; i < pIds.length; i++) {
ids.push(pIds[i].id);
}
// pIds is an array of objects [{id: 2, name: 'dd'}]
this.webClient = new Frisbee({
baseURI: `${Secrets.url_host}/api/v1`,
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
getData({ continuation = 1, q = '', ids = '' }) {
return this.webClient.get('/whatever', {
body: {
'platform_ids': ids,
},
}).then(getContent);
}
after running this code if get an array of ids [2,3] for example
but when I pass it to the backend (ruby on rails ) it arrive like that
[{"0"=>"1", "1"=>"2"}]
What can I do to get it as ["1", "2"]? I've tried many solutions but nothing works.
I couldn't solve it , so i changed my backend to accept string with "," and parsing it to array in the backend .
for example
const ids = {id: 3, id: 4};
const idsForBackend = ids.map(id => id.id).join();

Categories