I'm trying to integrate useSWR in a next js project I'm working on.
I want to pass a config to fetcher as an argument. I have read about Multiple Arguments in the docs
but it's not returning the data for some reason. it is making the api request I can see that in the network tab.
not sure how to do this.
any suggestions?
const fetcher = async (url, config) => {
let res;
if (config) {
res = await fetch(url, config);
} else {
res = await fetch(url);
}
if (!res.ok) {
const error = new Error('An error occurred while fetching the data.');
error.info = await res.json();
error.status = res.status;
throw error;
}
return res.json();
};
const { data, error } = useSWR(
[
rolesUrl,
{
headers: {
Authorization: `Bearer ${user.token}`,
'Content-Type': 'application/json',
},
},
],
fetcher
);
After a very long debuging I found out. fetch is getting the config object.
and then makes the request to the api. then useSWR returns the response. which causes the component to re-render. the config object gets recreated.
useSWR thinks argument updated and make the api request again. that's why we don't get the data.
I have fixed this with useMemo hook
const config = useMemo(
() => ({
headers: {
Authorization: `Bearer ${user.token}`,
'Content-Type': 'application/json',
},
}),
[user.token]
);
const { data, error } = useSWR([rolesUrl, config], fetcher);
Related
This is the code I am using
// function calling the api endpoint within a button onClick event handler
async () => {
const response = await fetch('/api/foo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data)
})
const responseData = await response.json()
}
// /api/foo.js
import { ref, uploadString } from 'firebase/storage'
import { storage } from './firebase'
export default async function handler(req, res) {
const data = req.body
const storageRef = ref(storage, data.ID)
const uploadTask = uploadString(storageRef,
JSON.stringify(data.object)
).then(snapshot => {
res.status(200).json(
{ message: 'Saved!', severity: 'success' })
res.end()
}
)
}
When a request is sent to the above API endpoint, the console in vscode shows that a request was sent with this error: API resolved without sending a response for /api/foo, this may result in stalled requests.
What does this mean and how is it fixed?
Thanks in advance!
Edit - added async to handler function, but error still showing
I am trying to call fetch, but it's giving me an error as "TypeError: Failed to fetch". To fix this, I am now using Axios. but, Somewhere I read that fetch is poly-filled, and no need to use Axios. can anyone suggest any way with a fetch to work?
useEffect(() => {
if (myProfile) callAPI(true);
else callAPI(false);
}, [myProfile]);
const callAPI = async (isMyProfile) => {
const response = await fetch(
`https://abcd.com/api/user_profile/v1/mine`,
{
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
},
method: "GET",
}
);
const data = await response.json();
setData(data);
};
The above call API function was not working with fetch so I used Axios as below and it was working fine then. but what's wrong with fetch?
const callAPI = async (isMyProfile) => {
axios
.get(`https://abcd.com/api/user_profile/v1/mine`)
.then((response) => {
setData(response.data);
});
};
I am dealing with POST and GET request.
When user makes a POST request data gets stored in the DB.
In GET request I am retrieving the data from DB.
In my useEffect I am using the GET request to get the data. It works only when page renders for the first time. It does not update the state whenever I make a POST request . I have to manually refresh the page to get the new data. When I put the state in my dependency it keeps making the fetch request as long as I am on that component . Below is my code..
Post Request
The post request is being made from child component.
const addFolder = async() => {
if (!folderName) {
alert("Folder Name Required");
} else {
const projectId = props.match.params.projectId
console.log("Project Id ==> ",projectId) //use projectId to make a call to the server..
console.log(folderName)
await fetch(`http://localhost:8080/mirFolder/new/${projectId}`,{
method:"POST",
body: JSON.stringify({
"title":folderName,
}),
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
}).then((res)=>{
console.log(res)
// window.location.reload()
return res.json();
}).catch((err) =>{
console.log(err)
})
}
}
GET request function
const [state, setstate] = useState([]);
useEffect(() => {
const token = isAuthenticated().token;
const projectId = props.match.params.projectId;
getMirFolderbyProject(token, projectId).then((data) => {
if (data.error) {
console.log(data.error);
} else {
console.log("data ==>", data);
setstate(data);
}
});
}, []);
GET fetch Api
export const getMirFolderbyProject = (token, projectId) =>{
return fetch(`http://localhost:8080/mirFolder/by/${projectId}`, {
method: "GET",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
})
.then((response) => {
return response.json();
})
.catch((err) => {
console.log(err);
});
}
This is because when a server gets a POST it will not serve new data, if you want the new data to be displayed after the POST request make sure to serve the new data to the client in the POST request.
you should fill your dependencies array with the variables which on change you would want re-render the component
const [state, setstate] = useState([]);
useEffect(() => {
const token = isAuthenticated().token;
const projectId = props.match.params.projectId;
getMirFolderbyProject(token, projectId).then((data) => {
if (data.error) {
console.log(data.error);
} else {
console.log("data ==>", data);
setstate(data);
}
});
}, [token , projectId]);
As #Klump said, when you made the POST request and update the data in DB, you again need to make a GET request instead of fetching updated data fetched from the POST response in order to keep the consistency.
For this, once the POST request promise resolved, you can use an additional fetching state in order to make a GET request.
Please provide your complete implementation containing both GET and POST requests with all the necessary components to further work on it.
I've written a custom hook that uses SWR to retrieve data from my API whilst setting the 'Authentication' header for the request.
The hook is working fine for all successful requests but I want to be able to handle failed requests (400 status codes).
I'm able to access the status code with the result from const res = await fetch(url but how do I return the error in the error parameter to the caller of the hook?
import useSWR from 'swr';
export default function useAPI(path) {
const auth = useAuth();
const { data, error, isValidating, mutate } = useSWR(
!path ? null : `${process.env.NEXT_PUBLIC_API_URL}${path}`,
async (url) => {
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${auth.user.token}`,
accept: 'application/json',
},
});
return res.json();
}
);
return { data, error, isValidating, mutate };
}
From SWR Error Handling documentation:
If an error is thrown inside fetcher, it will be returned as error by the hook.
In your case, you can simply handle to 400 status code response in the fetcher and throw an error after the handling is done.
const { data, error, isValidating, mutate } = useSWR(
!path ? null : `${process.env.NEXT_PUBLIC_API_URL}${path}`,
async (url) => {
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${auth.user.token}`,
accept: 'application/json'
}
});
if (res.statusCode === 400) {
// Add your custom handling here
throw new Error('A 400 error occurred while fetching the data.'); // Throw the error
}
return res.json();
}
);
I've been having CORS error when deploying my app so I decided to try out Netlify Dev.
I followed all steps of the written tutorials but I keep getting errors without being able to identify whats wrong. I haven't even deployed it yet and right now I am having the 403 error, before this was the 500 internal server error.
Please let me know if you notice any obvious mistake on my part.
Here's the node-fetch code:
const fetch = require("node-fetch");
exports.handler = async function () {
const headers = {
"x-api-key": process.env.REACT_APP_MY_API_KEY,
"content-type": "application/json",
};
try {
const response = await fetch("https://api.crimeometer.com", { headers });
if (!response.ok) {
return { statusCode: response.status, body: response.statusText };
}
const data = await response.json();
return {
statusCode: 200,
body: JSON.stringify(data),
};
} catch (err) {
console.log(err);
return {
statusCode: 500,
body: JSON.stringify({ msg: err.message }),
};
}
};
Here's the frontend of my app (with the relevant code):
const initializeApp = useCallback(() => {
async function fetchData() {
try {
let req = new Request(
`./.netlify/functions/node-fetch?lat=${lat}&lon=${lon}&distance=10km&datetime_ini=${newdateyear}&datetime_end=${newdatenow}&page=1`);
const res = await fetch(req);
const info = await res.json();
setCrimes(info.incidents);
} catch (err) {
console.log(err);
}
}
}, [submit, lat, lon]);
useEffect(() => {
initializeApp();
}, [initializeApp]);
This is the error in my console:
This is where i am trying to fetch data from:
https://api.crimeometer.com/v1/incidents/raw-data?lat=${lat}&lon=${lon}&distance=10km&datetime_ini=${newdateyear}&datetime_end=${newdatenow}&page=1
It requires two headers to be set on frontend, which I have done in the netlify function
The API key is currently being added to the React side, where it sets a header and makes a call to the netlify function. But the API key is actually required at the netlify function to request the third party API.
So you have to move the request header from React to Netlify function with something like:
const headers = {
'x-api-key': API_KEY,
'content-type': 'application/json',
}
const response = await fetch(
"https://api.crimeometer.com/v1/incidents/raw-data",
{ headers }
);