Handling errors within custom SWR hook - reactjs

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();
}
);

Related

NextJS API Endpoint Error - 'API resolved without sending a response for /api/foo, this may result in stalled requests.'

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

How to catch axios api call error 401 in reactjs?

I am using axios to make apis calls in react. If there is no token provided or token got expired server sends the 401 status. I want to check that status on reactjs side.
But if i check err object in catch the status field is null.
Here is the code
try {
MyService.getIntet(reqBody);
} catch (err) {
handleUnAuthorizedResponse(err);
}
error returns this
Service function:
import axios from "axios";
static getIntent(reqBody) {
const url = `${this.intentionBaseUrl}/process`;
const options = {
headers: {
"Content-Type": "application/json"
},
};
return axios
.post(url, reqBody, options)
.then((res) => res.data)
}
How to handle 401 error ?
You need to wrap the trycatch in async function and await MyService.getIntet(reqBody) to catch the error. The status code is in err.response.status.
You could also just MyService.getIntet(reqBody).catch(handleUnAuthorizedResponse) if you don't want to wrap it in async function.
You can use .catch chaining function after .then to catch all errors.
Error object will contain a response object which will contain actual response received by API response with all meta information. But make sure to put a condition while accessing this object as errors caught from the then block will not have response key.
import axios from "axios";
static getIntent(reqBody) {
const url = `${this.intentionBaseUrl}/process`;
const options = {
headers: {
"Content-Type": "application/json"
},
};
return axios
.post(url, reqBody, options)
.then((res) => res.data)
.catch(error => console.log(error.response.status))
}

useSWR - How to pass config object to fetch

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);

NextJS useSWR XHR is succesful but variable undefined

In the browser console I get a succesful request XHR to the api endpoint and the data is returned, however when I do a console.log(data1) the variable comes back undefined. What do I need to do to return this data to a nextjs app using useSWR react hook?
const FromDate = "02/01/2020";
const ToDate = "09/30/2020";
const baseURL = `http://example.com/test_db/system.sql?query=`
const fetcher = (url) => fetch(url, {
mode: 'no-cors',
headers: {
// 'Content-Type': 'application/json',
"Access-Control-Allow-Origin": "*"
}}
).then(res => res.json())
function updateDates (FromDate, ToDate) {
const url = `http://example.com/test_db/system.sql?query=sqlD`
const { data1, error } = useSWR(url, fetcher)
console.log("updatefunction", data1)
return {
data1,
isLoading: !error && !data1,
isError: error
}
}
const { data1, isLoading, isError } = updateDates("01/01/2018", "12/31/2019")
UPDATE* The error that I receive is:
SyntaxError: JSON.parse: unexpected end of data at line 1 column 1 of the JSON data

Exception not thrown inside save saga

I am working on an SPA with redux-saga state management. My load and save methods themselves are working, yet there is a lot of weird stuff... Below is the saga code:
export function* getEventDetails({ id }) {
const requestURL = `${url}/admin/event/${id}`
try {
const event = yield call(request, requestURL)
yield put(eventLoaded(event, id))
} catch (err) {
yield put(eventLoadingError(err))
}
}
export function* saveEventDetails({ event }) {
const id = event['id']
const requestURL = `${url}/admin/event/${
!isNaN(id) && id !== undefined && id !== null ? id : 'new'
}`
try {
const createdEvent = yield call(request, requestURL, {
method: !isNaN(id) && id !== undefined && id !== null ? 'PUT' : 'POST',
body: JSON.stringify(event)
})
yield put(eventSaved(createdEvent, createdEvent['id']))
yield put(loadEvent(createdEvent['id']))
yield put(loadPreviousEvents())
yield put(loadUpcomingEvents())
} catch (err) {
console.log('caught error inside saga')
yield put(eventSavingError(err))
}
}
export default function* eventsData() {
yield takeLatest(LOAD_EVENT, getEventDetails)
yield takeLatest(SAVE_EVENT, saveEventDetails)
}
One thing is definitely strange - if I turn off the API server then try saving, I never see caught error inside saga in the console. I am therefore unable to dispatch the eventSavingError action, etc.
Where is my error action? In the console I see:
reducer.js:48 action: {type: "project/Container/SAVE_EVENT", event: {…}}
request.js:55 PUT http://localhost:5000/event/10 net::ERR_CONNECTION_REFUSED
The request function:
function checkStatus(response) {
if (response.status >= 200 && response.status < 300) {
return response
}
const error = new Error(response.statusText)
error.response = response
throw error
}
export default function request(url, options) {
const headers = {
Accept: 'application/json',
'Content-Type': 'application/json',
'Access-Control-Request-Headers': 'Content-Type, Authorization'
}
const token = localStorage.getItem('token')
if (token) {
headers['Authorization'] = `Bearer ${token}`
}
const newOptions = {
...options,
mode: 'cors',
headers
}
return fetch(url, newOptions)
.then(checkStatus)
.then(parseJSON)
}
Using #oozywaters suggestion, I tweaked the code as:
return fetch(url, newOptions)
.then(checkStatus)
.then(parseJSON)
.catch(err => {
throw err
})
It does fix the problem with the missing exception.

Resources