Get cookie/localStorage value in nextjs API request - reactjs

I'm trying to get cookie or localStorage value in my API request. When I'm trying to access localStorage I get error that localStorage is not defined, cookies are undefined, I tried my luck with AsyncLocalStorage, but I'm getting error that window is undefined. There is any way to get saved value in this asynchronous function?
import AsyncLocalStorage from '#createnextapp/async-local-storage'
export default async (req, res) => {
try {
let data = await AsyncLocalStorage.getItem('#key')
console.log(data)
}catch(error){
console.log(error)
}
}

This is expected. When you process the data in your API it is actually running in the server. Localstorage, and cookies however are browser based storage (This is also why window is undefined, servers don't have windows). As a result they are not available to the API to leverage. Instead when you make the API request you need to add the data into a header which can then be parsed on your server.
Setting a header on your request:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/setRequestHeader
From there you should be able to read the headers through:
(req, res) => {
// look at all these great headers we have
const { headers } = req;
}

Related

Django server unable to get data from react Axios

I'm not getting an error but when I saw the logs of my server it prints an empty object {} whenever I send a request to the sever from my react app using axios. I double checked everything every other request in another components of my app works fine, but only in this particular request the data is not being sent! I have no CORS issue!
My react axios request
// PrivateAxios instance to send api request
const axiosPrivate = useAxiosPrivate();
const handleSearch = async () => {
const data = JSON.stringify({ from_company: keyWord });
try {
const response = await axiosPrivate.get(SEARCH_URL, data);
console.log(response);
setRecords(response?.data);
} catch (err) {
if (!err?.response) {
console.log("NO SERVER RESPONSE");
} else {
console.log("SOMETHING WRONG");
}
}
};
Server log
{} <-- Prints the request.data as an empty object
"GET /api/find_many/ HTTP/1.1" 200 6276
The django server responses with correct details when I send a request with Postman or Thunder Client. The server also prints the object that were sent with the Postman request. I don't know why the server is unable to get the object or data when I request from my react app.
Request sent from Postman returns
{'from_company': 'Jethmal Paliwal'} <-- Prints the request.data correctly
"GET /api/find_many/ HTTP/1.1" 200 2284
I have double checked everything, my headers are set correctly, Content-Type: application/json, withCredentials: true, and every other possible settings, Even every request from other components works great, but why this particular request doesn't reach the server?
Tried writing the data as an Object in the request funcion itself
const response = axiosPrivate.get(SEARCH_URL, { "from_company": "Jethmal Paliwal" }); which doesn't work as well. The same empty object gets printed.
Tried JSON.stringify the data, which doesn't work as well.
I believe that axios is omitting the data as it's not per REST standard to submit data in the GET request. HTTP allows that, but people and apparently libraries are not expecting it.
This is the API for axios get method:
axios.get(url[, config])
as you see there is no data in the method signature. And if we look at the POST method:
axios.post(url[, data[, config]])
I suggest if you have data to submit to server that you use a POST method instead.

Post Request from axios always returns Unauthorized despite having valid JWT set in header/Axios Deletes Headers

I set up passport-local to login a user, and then once logged in, the user will be given a JWT token through passport-JWT. The JWTStrategy is set up to use
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken() so that the JWT can sent with the Authorization header Authorization: `Token ${userContext.token}`}. In my react client side, I have set up a GET request using axios as shown here:
const fetchProfileDetails = async(config)=>{
const res = await axios.get("http://localhost:8080/users/me", config)
}
const config = {
method:"GET",
withCredentials: true,
headers: {Authorization: `Bearer ${userContext.token}`}
}
This request successfully authenticates and returns the user data from /me.
Now heres the kicker: when I use the exact same request structure, but switch the method to post in the axios request and in my express route in the backend, the request always responds with a 401 Unauthorized.
However, when I send the same request from POSTMAN, with the same Bearer Token used in the request that was Unauthorized, the request succeeds without any errors.
TLDR: GET requests work with the JWT token and return with a 200 status code, while the same request with a POST method returns with a 401 status code.
What am I missing here??!
You are probably using there GET and not using POST anywhere.
In your code, you have code only for get. You will need to write code for post as well.
Below is the code for post for your reference:
router.post('/', config, async(req, res, next) => {
const { error } = validateBody(req.body);
if (error) {
return res.status(400).send(error.details[0].message);
}
const newData= new passport({ name: req.body.name });
await newData.save();
console.log('saving the document');
res.send(newData);
})
Your code should have post as well. Writing single code will not work. You need to have to write code for every condition and every possibility. So like for get need code for post as well, also if you have condition for patch or delete or put you will have to write the axios method for that as well.
Hope this has helped you in any way.
I have come upon a solution for this issue. For some reason, axios was not maintaining the Authorization header I had set in my config variable, and deleted it upon making the request. To solve this, I just had to reshuffle my axios request to look like this:
const res = await axios({
method:'POST',
url:"http://localhost:8080/users/test",
headers:{'Authorization':`Bearer${token}`
}})
I feel cheated as I spent a ton of time on this and the solution was so underwhelming. Axios was trolling me the whole time :/

Reading response header in onError in Apollo React

In React Apollo client, we have some links configured.
const apollo = new ApolloClient({
cache: new InMemoryCache(),
link: from([
onErrorLink,
afterwareLink,
httpLink
])
});
In our application, when session times out we return the response with 200 status and some response headers (timeout: true). By using these response headers we need to show some dialog on UI. The response header also has content-type: text/html. Also remember, sessions are maintained on federal level (SSO) in our organization and we do not control response context for that.
When session times out and we receive response from server, Apollo react client onError link fires up and we try to access response as below.
const context = operation.getContext();
const response = context.response;
const headers = response.headers;
In above code, context never has response property and hence response is undefined. Due to this, I am not able to access header values.
Analysis
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const response = context.response;
const headers = response.headers;
return response;
})
};
I have seen in case of error, only onError is resolved and afterwareLink map function never triggers.
response is populated in context only if comes via forward function given by Apollo.
In successful API calls, afterwareLink map function do resolves and I am able to find all headers just fine.
Solutions online are similar but just does not work.
I am stumped here. I can clearly see response headers in Dev Tools and Fiddler. It's for sure there. Just Apollo is not allowing me to access that. In my case, networkError or graphQLErrors are not of much use.
This was few steps problem to solve. Sharing my findings and solutions below.
Headers
Make sure the response from API has the following header value.
Access-Control-Allow-Origin: http://uiclient.com
Access-Control-Expose-Headers: *
The first header will make sure the browser is satisfied and allows UI to resolve the response. The second header will make sure UI can read the response headers.
Response Content Type
In my case response type was not as expected by Graph and cannot be changed as this was from federal layer (SSO). Others can face the same problem in the case of third party service, 401, 403 or other status codes.
To resolve the problem, I wrote a simple custom fetch function and attached it to HttpLink.
const httpFetch = (uri, options) => {
return fetch(uri, options).then(response => {
// ... read headers from response.headers.
if (response.status >= 500) { // or handle 400 errors
return Promise.reject(response.status);
}
return response;
}).catch(error => {
return Promise.reject(error);
});
};
const httpLink = new HttpLink({
uri: 'https://serviceapi.com',
fetch: httpFetch
});
Handle httpFetch
This technique can be used to read data in Apollo client when your response is not JSON (or is JSON). In my case, I checked the header first and if the header had session timeout value then resolved the promise using a custom object (as the main content will be HTML).
The above steps will mostly help you to customize any response in Apollo. Also note, I did not write any custom middleware (or link) to address this issue.

React App, Express Server - Set cookie not being stored in browser

The long short is...
My express backend sets a cookie and sends it back to the client, everything works great when I use Postman and my React Native app. The issue is the web version which is done via ReactJs makes the same request but cookie is stored in the browser.
So I know cookies are working but not for the web version which is strange when I created a test endpoint http://localhost:3000/server and call it straight from the browser the cookie is stored.
So this is my code doing a simple fetch to the server which then sends back a cookie:
const fetchData = async () => {
try {
const res = await fetch(`http://192.168.0.11:3000/server/`, {
credentials: "include",
});
if (res.ok) {
const resData = await res.json();
console.log({ resData });
}
} catch (err) {
console.log({ err });
}
};
The request came back successful but no cookie is stored
Access the same endpoint from the browser directly results in this:
An extract from the response header while being sent shows that the cookie was sent in the response just not stored in the frontend
Was a pretty simple fix
I was using http://localhost:3001 for the react app I just simply used the ip address instead http://192.168.0.11:3001

Fetch GET request hitting .catch() on anything but 200 response

I have a redux store set up with actions to handle loading accounts. The action calls a service like so:
const requestOptions = {
method: 'GET',
headers: authHeader()
};
return fetch(`http://localapi.co.uk/api/account/load/${account_id}`, requestOptions)
.then(handleResponse)
.then(account => {
if(account.account.id) {
localStorage.setItem('account', JSON.stringify(account))
}
return account;
})
.catch(redirectToLogin)
Handle response is simply a function that checks the .status and .ok properties of the response and either displays an error or logs out if the response status is 401. This works perfectly fine for POST requests. When I hit my login route, any response hits the first .then(handleResponse) and deals with it.
When I send a GET request instead like above 404s, 401s, 500s.. etc all skip the .then(handleResponse) and instead jump to my catch. The problem that causes is that because catch doesn't actually give me a response object to work with I can't check the status - I want to do different things depending on whether the get was a 401 (I want to logout) or a 500 (I want to display a user error stating what went wrong) for example.
Is there a solution that will allow me to get a response or stop my GET requests hitting the .catch and instead hit the response handler I've written?
I'm using:
a Laravel 5.7.20 back-end
a React 16.7.0 front-end
running local node server with npm start
How about having a clean async/await function. Waiting till you JSON data becomes ready and then having the rest of your if logic or returning the account object from the function and moving the rest of the logic to the caller function. Something like this:
async myFunc({ account_id}) {
const url = `http://localapi.co.uk/api/account/load/${account_id}`;
const response = await fetch(url, { headers: headers: authHeader() });
const account = await response.json();
// return account (recommended);
// Place your if logic here (not recommended because its not clean)
}

Resources