How to make params in requests by axios into objects? - javascript

I have this code on a ReactJs app:
axios
.get(`/shipping/get-shipping-values`, {
params: {
products: [
{
...product,
quantity,
},
],
postalCode,
cartTotalPrice: getProductPriceNumber(product.price) * quantity,
},
})
.then((response) => {
// do things with response
})
.catch((error) => {
console.log(error);
});
This is the console log of the products param in the express:
[ //Note above the ' that transform this object in an string.
'{"package":{"weight":0.067,"width":5,"height":2,"length":2},"quantity":945}'
]
Console log of products in the frontend:
[ package: { weight: 0.067, width: 5, height: 2, length: 2}, quantity: 945 ]
As you can see, is an array but the object inside this array is an string.
I didn't change the default header of the axios, so is set to aplication/json.
I could use an JSON.parse() to make this string into an object. But I want to know if there is a way to make this automatic. That way I will not need to JSON.parse() every param that I send to the express server.

If you are using expressJS add app.use(express.json()); middleware, before the routes and you can find the entire object inside the req.body object, inside the route handler.
Let me know if this helps.

Related

Mocking React Query useQueryClient to test cached data

I use a custom hook to share an increment function accross my app (it increments quantities in a shopping cart).
What that function does :
gets a data object from a React Query cache
increments the data quantity property
makes some API call via React Query useMutation then, on success, updates the React Query cache
After that, the components reads the React Query cart cache and updates the UI with the new quantity.
The hook does the job and finally the cache is updated as expected.
Now, I try to test the hook in a component to check that the UI is updated accordingly.
The API call is mocked using msw. Its returned value is used to update the cache :
rest.put(`${api}/carts/1`, (req, res, ctx) => {
return res(ctx.json({ data: [{ id: 1, quantity: 2 }] }));
})
I also mocked the react-query queryClient.setQueryData and getQueryData functions so I can test their returns values.
jest.mock("react-query", () => ({
...jest.requireActual("react-query"),
useQueryClient: () => ({
setQueryData: jest.fn(),
getQueryData: jest
.fn()
.mockReturnValueOnce({ data: [{ id: 1, quantity: 1 }] })
.mockReturnValueOnce({ data: [{ id: 1, quantity: 2 }] }),
}),
}));
Finally, I test the UI that should updates with the new quantity, but the mocked getQueryData always return the original quantity: 1, even with multiple call.
Now I'm not sure I have the right approach for that test.
Suppose I just want to mock getQueryData for a particular test case and leave the other functions like invalidateQueries, cancelQuery, and setQueryData as it is, then how can modify this mock function?
This is what I wrote. But getting this
TypeError: queryClient.cancelQueries is not a function
jest.mock("#tanstack/react-query", () => ({
...jest.requireActual("#tanstack/react-query"),
useQueryClient: () => ({
// setQueryData: jest.fn(() => ({ data: [{ label: 'Blue', id: 34 }] })),
// cancelQueries: jest.fn(),
// invalidateQueries: jest.fn(),
...jest.requireActual("#tanstack/react-query").useQueryClient(),
getQueryData: jest
.fn()
.mockReturnValueOnce({ data: [{ id: 1, quantity: 1 }] })
.mockReturnValueOnce({ data: [{ id: 1, quantity: 2 }] }),
}),
}));
Why would you need to mock setQueryData and getQueryData ? Mocking the network layer with msw should be all you need. If you wrap your rendered hook in a QueryClientProvider with a queryClient, that will be populated with the mocked data returned from msw, and queryClient.getQueryData will be able to read it without mocking it.

Axios turns nested JSON array to empty when posting

I'm trying to post data from Axios to my API but although my json data looks correct when passing to Axios, browser tools tells me that my nested property is empty.
My data contains nested objects and looks like this:
{
'name': 'foo',
'index': 1,
'nested': [
{
'date': '2020-05-10',
'geojson_data': {
'crs': Object,
'name': 'my-name',
'type': 'FeatureCollection',
'features': [{ ... }, { ... }]
}
},
]
}
Edit: geojson_data results from parsing .geojson file, thus, the features array contains ~300 items.
My axios function is defined here:
async post(data) {
console.log(data);
return axios
.post(API_URL + 'endpoint/',
data,
{
headers: authHeader()
})
.then(response => {
return response.data;
})
}
authHeader() is used to provide Bearer token authorization.
I check that data passed to axios is correct (looks like above), but browser tools tell me that data actually sent looks like
{"name":"foo","index":1,"nested":[]}
Why is my array empty ?
Edit: I tried to manually populate a sample nested object and it seems to work. Yet, whenever I use my actual data, it doesn't.
Don't know if it's relevant but in the console, here is the difference I can see between the 2 collapsed objects (actual vs sample):
Sample : Object { name: "foo", index: "1", nested: (1) […] }
Actual : Object { name: "foo", index: "1", nested: [] }
Notice the nested array which looks like it is empty. Yet, if I expand it, they look the same. Any ideas ?
P.S: looks like this SO post from 3 years ago, but without solution

How to get nested objects using Firestore REST API?

I'm sending REST API requests using axios package.
I can get a single document (for example, cities/cityId):
axios.get(`https://firestore.googleapis.com/v1/projects/<PROJECTIDHERE>/databases/(default)/documents/<COLLECTIONNAME>/<DOCID>`)
What I can't do is to get a nested document (for example, cities/cityId/streetId)
The database structure is very simple.
cities: { // COLLECTION
cityId: { // DOCUMENT
streetId: { // MAP
buildingId: '...', // STRING
...
},
...
},
}
This article Google Firestore REST API examples suggests that it's possible to get nested objects using structured queries. Unfortunately, I've been trying to do it without any success.
Here's my not working code:
getQuery ({ path, token }) {
const url = 'https://firestore.googleapis.com/v1/projects/big-boobs/databases/(default)/documents:runQuery'
const params = {
from: [ { collectionId: 'cities' } ],
select: {
fields: [
{ fieldPath: 'cityId1.streetId' }
]
}
}
const options = {
method: 'get',
url,
params,
paramsSerializer: function (params) {
return Qs.stringify(params, { arrayFormat: 'brackets' })
}
}
if (token) options.headers['Authorization'] = `Bearer ${token}`
return axios(options)
}
I'm getting an error:
{
"error": {
"code": 400,
"message": "Invalid JSON payload received. Unknown name \"from[][collectionId]\": Cannot bind query parameter. Field 'from[][collectionId]' could not be found in request message.\nInvalid JSON payload received. Unknown name \"select[fields][][fieldPath]\": Cannot bind query parameter. Field 'select[fields][][fieldPath]' could not be found in request message.",
}
You can select individual fields from a document, but you can't select individual properties from a map field. I think you'll have to settle for getting the entire map called streetId.
If it's unacceptable to pull down the entire map, you can reorganize your data such that each streetId exists in its own document.

Filtering away javascript objects into an array when using fetch

I have a react application, where I use the axios library, to get some values, and set them into an array of javascript objects in my state
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data}))
}
Now I want to check if the objects, contains an Owner object, inside it, and filter out does that does,
First, I tried making a const, and then using the filter, to check if they contain the objects, and then set the state, but I can't save my values in a local variable
componentDidMount(){
const animals= [];
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => animals=res.data)
console.log(animals) // logs empty array
console.log('mounted')
}
how can I make it so, that I can only get the animals that do NOT, have an owner object inside it?
Your animal array is empty in your second example because axios.get is asynchronous, what is in your then will be executed once the data is fetch, but the function will keep on going in the meantime.
To filter out your array, simply use filter right after fetching your data :
componentDidMount(){
axios.get('http://localhost:8080/zoo/api/animals')
.then(res => this.setState({animals: res.data.filter(animal => !animal.owner)}))
}
This function will filter out every animal object that does not have an owner property.
Working example :
const animals = [
{
name: 'Simba',
owner: {
some: 'stuff'
}
},
{
name: 1
}, ,
{
name: 2
}, ,
{
name: 3,
owner: {
some: 'stuff'
}
},
{
name: 'Bambi'
//status: 'dead'
}
]
console.log(animals.filter(animal => animal.owner))
EDIT: the answer was changed so that it only filters animals, that does not have an owner

Sails.js res.json not returning deep properties when using nested-pop

I have this code to get some stock data with deep nested associations by using nested-pop:
Stock.find({limit:100, sort:'name ASC'})
.populate('scans', {limit:1,sort:'createdAt DESC'})
.then((stocks) => {
return nestedPop(stocks, {
scans: ['values']
})
}).then(stocks => {
console.log(stocks[0].scans[0]);
res.json(stocks);
})
In the server console the scan is logged and the values array is printed. But res.json returns the stocks with scans that are all missing the values array.
Expected output:
[{
id: 1,
scans: [
{
id: 1,
values: [
{
id: 1,
value: 0.5,
type: 1
},
...
]
}
]
}, ...]
The returned output:
[{
id: 1,
scans: [
{
id: 1,
}
]
}, ...]
The error doesn't seem to be related to the depth of the object, because I tested it with some deeper test object and everything was returned. I just don't understand how this is not returned by res.json if it's there.
UPDATE:
I made other tests now and found out that both, .toObject and .toJSON called on a single stock cause the values to disappear on the server side. And because res.json is calling .toJSON on the passed objects it also doesn't appear in the browser.
nestedPop is returning values that can not be parsed correctly by .toJSON, because they are already converted.
I solved the problem by converting all stocks before I pass them into nestedPop, so the working code is:
Stock.find({limit:100, sort:'name ASC'})
.populate('scans', {limit:1,sort:'createdAt DESC'})
.then((stocks) => {
stocks = stocks.map((s) => s.toJSON());
return nestedPop(stocks, {
scans: ['values']
})
}).then(stocks => {
console.log(stocks[0].scans[0]);
res.json(stocks);
})
Now everything is logged on the server side AND passed to browser.

Categories

Resources