Axios 'GET' request is pending but it never reaches the server - node.js

I am using axios on my React app to get data from my server (Node). My GET request stays pending in chrome developer tools and does not reach the server if I refresh the app on the provided route (e.g., http://localhost:3000/category/5d68936732d1180004e516cb). However, if I redirect from the home page, it will work.
I have tried different variations of making sure I end my responses on the server end of things.
Several posts have had related problems (e.g., request not reaching the server, POST request does not reach the server) but unfortunately not been helpful in my situation.
Here is my main call in my react app:
componentDidMount() {
console.log('I am here!'); // this gets executed even on page refresh
axios.get(`/api/categories/${this.props.id}`)
.then( (res) => {
this.setState({
title: res.data.category.title,
module: res.data.category.module ? true : false,
...res.data
})
}, (err) => console.log(err))
.catch(err => console.log(err));
}
On my back end I call this function after going through user verification:
module.exports.publishedCategories = async function(req, res) {
try {
// some code that I removed for clarity
res.json({
category,
children,
flagged: flaggedCategories
});
} catch(err) {
console.log(err);
res.sendStatus(500).end();
}
}
Some more code regarding my routing:
index.js
<Route
path='/category/:id'
render={ (props) => {
return <Category id={props.match.params.id} />
}}
/>
I do not get any error messages...

I was overzealous with my initial solution of switching to componentDidUpdate(). This only worked for page refreshes but not for redirects (i.e., I had the reverse problem). My final solution is as follows:
componentDidMount = async () => {
setTimeout( async () => {
this.loadCategory();
}, 10)
}
componentDidUpdate = async (props, state) => {
if(props.match.params.id !== this.props.match.params.id) {
this.loadCategory();
return;
}
return false;
}
loadCategory = async () => {
let result = await axios.get(`/api/categories/${this.props.match.params.id}`);
this.setState({
title: result.data.category.title,
module: result.data.category.module ? true : false,
...result.data
});
}
I am not sure why adding a setTimeout() works for componentDidMount().
As per the documentation componentDidUpdate(), it is good for netork updates however you need to compare prior props with current props.
Unforunately I am not sure how to get around the setTimeout() hack but it seems to work.

Related

Testing Angular Services with MSW causes timeout

EDIT: I should mention, that I only have problems during testing. When I run ng serve and use msw to serve the data everything works correctly.
I stumbled upon mswjs recently and wanted to use the mock service workers to test my frontend services without waiting on the backend team and avoid having to write mock-service classes. I setup everything according to the examples provided in the documentation.
At first I got the message that stating spec 'UserService should get list of users' has no expectations.
I researched this and added a done() function call at the end of my subscribe callback. After doing that, I get the following error:
Error: Timeout - Async function did not complete within 3000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL) in node_modules/jasmine-core/lib/jasmine-core/jasmine.js (line 7609)
I already tried increasing the default_timout in Karma but even setting it to 30.000 did not change the result.
I also tried working around by using waitForAsync without any success. This way I get no error and the test succeeds but only because it still finds no expectations within the spec.
Most example I found online do not deal with mock service workers and instead resort to using mock-services and fakeasync which does not help in my case.
This is how my code looks like:
My Angular Service:
#Injectable({
providedIn: 'root'
})
export class UserService {
private url = 'http://localhost:3000/api/users';
constructor(private http: HttpClient) { }
getUser(id: string): Observable<User> {
return this.http.get<User>(`${this.url}/${id}`);
}
listUsers(): Observable<User[]> {
return this.http.get<User[]>(this.url);
}
}
My Test Code:
describe('UserService', () => {
let service: UserService;
beforeAll(() => {
worker.start();
});
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
});
service = TestBed.inject(UserService);
});
afterAll(() => {
worker.stop();
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should get list of users', (done) => {
service.listUsers().subscribe((data) => {
expect(data.length).toBe(5);
done();
});
})
});
The Worker setup:
const handlers = [
rest.get('http://localhost:3000/api/users', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json(users))
})
]
export const worker = setupWorker(...handlers)
I managed to solve my own problem by using firstValueFrom and waitForAsync.
I changed the code in my tests to the following:
it('should get list of users', waitForAsync(async () => {
const source$ = service.listUsers();
const result = await firstValueFrom(source$);
expect(result.length).toEqual(5);
}));

Having trouble Updating an item with react-admin <Edit/> component

Trying to get a simple edit view to update products. Right now I can successfully click the edit button on a product item in a list and the edit view renders with the product data passed into the form. However, when I edit a field and hit the save button the getOne route handler on my backend is hit while the save button stays permanently pressed with a loading symbol on it.
Meanwhile the onSuccess function for my <Edit /> throws an error in the JavaScript console saying its data prop was undefined leading me to believe nothing was returned to the component. By setting breakpoints and console.logs I'm pretty confident that the update method is being hit by the save button but I can't figure out why the update method would be hitting the getOne route handler on the backend and why it wouldn't be receiving anything back from it. The only thing I've really tried aside from basic tinkering and console logging was to give unique routes to update because the url is the same as getOne it just has a different method but it just caused more errors.
You can clone the repo or scroll down for code snippets
Git clone link: https://github.com/cbddy/wholesale-portal.git
Steps to reproduce:
start up the app by running npm run buil, npm run server then go to http://localhost:5000/admin#/admin-products. You must be on react-admin branch (git checkout react-admin). Also, you must rebuild and restart the server after every change.
click the show button on one of the items in the list
click edit in top right corner of the show comp
edit one of the fields and hit save
You will see the getOne route handler firing in the server and the onSuccess func throw it's error in the js console in Chrome
relevant files:
frontend
src/components//adminSetup/dataProvider.js
src/components//adminSetup/Setup.js
src/components//adminSetup/MenuItems/Products.js
backend
-server/server.js
-server/admin-routes/admin-product.router.js
React-admin version: 3.7.0
Last version that did not exhibit the issue (if applicable):
React version: 16.13.1
Browser: Google Chrome
Stack trace (in case of a JS error): ?
Here is the relevant code and errors.
edit component
export const ProductEdit = props => {
const notify = useNotify();
const refresh = useRefresh();
const redirect = useRedirect();
const onSuccess = ({ data }) => {
notify(`Changes to product "${data.name}" saved`)
redirect('/admin-products');
refresh();
};
return (
<Edit onSuccess={onSuccess} actions={<ProductEditActions />} title={<ProductTitle />} {...props}>
<SimpleForm>
{/* {!} NEEDS TO BE DONE LATER TO EDIT ORDERS OF USERS */}
{/* <ReferenceInput source="orderId" reference="orders"> */}
<TextInput label="Product Name" source="name" />
<TextInput label="Category" source="category"/>
<TextInput label="Description" source="description" options={{ multiLine: true }} />
<NumberInput label="Price" source="price"/>
{/* <ImageInput source="imageData"/> */}
</SimpleForm>
</Edit>
)
}
DataProvider methods for getOne and update
getOne: (resource, params) => {
console.log("getOne resource", resource);
return httpClient(`${apiUrl}/${resource}/${params.id}`)
.then(({ json }) => {
console.log(json);
return { data: json };
})
},
update: (resource, params) => {
console.log("update", resource);
return httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: "PUT",
body: JSON.stringify(params.data),
}).then(({ json }) => {
return { data: json }
}).catch(err => {
return displayErrorMessage(err)
})
},
backend route handlers for getOne and update
//getOne
router.get("/:id", async (req, res) => {
console.log("req method :", req.method)
console.log("req.data :", req.data)
console.log("getOne hit. Id: ", req.params.id)
await Product.findOne({_id: req.params.id})
.then(async (product) => {
product = await JSON.parse(JSON.stringify(product).split('"_id":').join('"id":'));
console.log("parsed product: ", product)
res.json(product)
}).catch(err => {
console.log("error: ", err)
res.status(500).send("user not found.")
})
})
router.put("/:id", async (req, res) => {
console.log("update hit")
console.log("id: ", req.params.id)
console.log("body: ", req.body)
await Product.updateOne({_id: req.params.id}, req.body)
.then(async (product) => {
product.save();
product = await JSON.parse(JSON.stringify(product).split('"_id":').join('"id":'));
return res.status(200).json(product)
}).catch(err => {
console.log(err)
res.status(500).send("Failed to update.")
})
})
My browser directly after hitting save

AngularJs $http request stays pending and does not return value from the database

I am currently writing a route which allows me to recieve information from a stored procudre I have in a database. I have written a request in AngularJS and a route in NodeJS but I am just recieving a pending request in the chrome Network developer window. I can see that the console.log in the NodeJs app has the data I require so it has retrieved it but there is nothing coming back in any of the console logs in the the AngularJS app.
Here is the code for the both the angularJS app and the Node App:
AnglaurJS:
checkForReplenishmentEmptyTrolley = async () => {
LIBRIS.extensions.openLoadingModal();
console.log('in checkForReplenishmentEmptyTrolley');
try {
const varPromise = await $http.get(`${LIBRIS.config.stockService}stockMovement/checkForUnattachedTrolley`)
.then((response) => {
console.log(response);
// Request completed successfully
}, (error) => {
// Request error
console.log(error);
});
console.log(varPromise.data);
// 1. check that there are no ghost replenish - lines 1-15
console.log('in try/catch');
console.log('promise', varPromise);
} catch (error) {
console.log(error);
}
},
NodeJS code:
app.get(`${ROUTE}/attachTrolley`, async function(req, res){
const newRequest = await DB.newRequest();
console.log('we have made it to the route');
try {
console.log('we have made it to the Try/Catch route');
newRequest.input();
const record = await newRequest.execute('dbo.usp_STK_CheckForUnattachedTrolley');
res.json(record)
console.log(record, 'record');
} catch (err){
handleError(res, err);
console.log(err);
}
});
The problem is that you are doing a .then on a awaited promises and not returning anything from that. You have two choice here
Either return response from then so when you try to access the value here console.log(varPromise.data); it works.
Or remove the .then alltogather as it is not required because you are awaiting it any ways.
Basically just do this
checkForReplenishmentEmptyTrolley = async () => {
LIBRIS.extensions.openLoadingModal();
console.log("in checkForReplenishmentEmptyTrolley");
try {
const varPromise = await $http.get(`${LIBRIS.config.stockService}stockMovement/checkForUnattachedTrolley`);
console.log(varPromise.data);
// 1. check that there are no ghost replenish - lines 1-15
console.log("in try/catch");
console.log("promise", varPromise);
} catch (error) {
console.log(error);
}
};
Hope this fixes your issue.
Solved it! I had no return statement in my route!

Using Node and Express, How to Call remote API from inside server.get(..)

Because of CORS problems, I want to call an external REST API from inside my node express server. That is, I have code like this that obviously does not work because it does not return.
How can I make this work and return the results of my external call?
const server = express();
server.put('/callme',(req,res) => {
axios
('http://weather.com/restapi', 'put', { zip: 10530 })
.then((resp: any) => {
console.log(' success' + resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
Axios returns a Promise which is resolved in the .then(). In order to get the response data back to the client you need to return it with res.send().
const server = express();
server.get('/callme', (req, res) => {
axios
.get('http://weather.com/restapi?zip=10530')
.then((resp: any) => {
res.send(resp.data);
})
.catch(function(error: any) {
console.log(error.message);
});
}
It would be a good idea to cache the weather API response for a period of time and serve the cached response for subsequent requests.

Error:Can't set headers after they are sent in express

I am exploring node and express with redux where I want to set a cookie after the page has been rendered and wanna use the updated state for setting a cookie where I am getting this error.
Please help me in getting the answers of following ?
1) Let me know wheather the syntax I've written is correct or not and if not ,then what should be done?
2) How can i set a cookie to response after successful render of ejs file?
router.get('/dashboard',isLoggedIn,(req, res) => {
store.dispatch(initialize(reduxOauthConfig))
.then(() => match({ routes: dashroutes, location: req.url }, (error, redirectLocation, renderProps) => {
if (redirectLocation) {
res.redirect(301, redirectLocation.pathname + redirectLocation.search);
} else if (error) {
res.status(500).send(error.message);
} else if (!renderProps) {
res.status(404).send('Not found');
} else {
loadOnServer({ ...renderProps, store })
.then(() => {
const componentHTML = ReactDOMServer.renderToString(
<Provider store={store}>
<ReduxAsyncConnect {...renderProps}/>
</Provider>
);
const initialState = store.getState();
res.render('dashboard.ejs', {
markup: componentHTML,
intialState:initialState
});
})
.then(html => {
// !!! IMPORTANT TO PERSIST SESSION IF JavaScript failed to load / initialize
res.cookie('authHeaders', JSON.stringify(getHeaders(store.getState())), { maxAge: now() * 0.001 + 14 * 24 * 3600 });
res.end(html);
})
.catch(err => {
console.log(err.stack);
res.end(err.message);
});
}
}));
});
This error is caused by trying to send more of a response after the response has already been sent and finalized. This issue is often triggered in people's code by faulty async handling in request handlers.
In your promise chain, you are doing res.render(), then res.cookie(), then res.end(html).
res.render() all by itself sends the response. The following two commands are then trying to send a response when a response has already been sent, thus you get the error you are seeing.
Also, your second .then() handler after loadOnServer() appears to be expecting an argument you named html, but the previous .then() handler does not return anything so that html argument will be undefined.
It is not clear to me what you intend for the logic to be here since you seem to be trying to send rendered HTML twice in the same promise chain.

Resources