I'm using useSWR hook in components. Under api folder I have api. I'm using app router
.
Components are imported in page.
Problem is that if API encounters some error then how should I return it to the components which are using useSWR hook.
Components rely on {isLoading,error,data,mutate}
destructured from useSWR hook
to show various states.
So how should I return error so that useSWR hook's error state gets set.
I don't want to pass data :{error : ...} as it causes useSWR hook's data state to be set which creates rendering problems.
On error my component also allows user to Retry by calling mutate
NextJS Version: 13.5.4
Folder Structure :
projectName
├── .gitignore
├── app
│ ├── api
│ │ └── tryapi
│ │ └── route.js
│ ├── components
│ │ └── Try
│ │ └── Try.js
│ ├── error.js
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.js
│ ├── not-found.js
│ ├── page.js
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── README.md
└── tailwind.config.js
route.js under app\api\tryapi\route.js
import { NextResponse } from 'next/server'
export async function GET() {
try {
console.log("tryapi !", new Date().toLocaleTimeString())
let data = await fetch('https://dummyjson.com/users')
// let data = await fetch('https://dummyjson.com/user12d1d')
// TO SIMULATE A ERROR IM PASSING WRONG URL
let UserData = await data.json()
// console.log(UserData.users);
return NextResponse.json({ data: UserData.users })
} catch (error) {
// return NextResponse.json({ data: { error: true } })
return error
// THIS THROWS ERROR WHICH IS PROPERLY HANDLED BY useSWR hook in component
}
}
Try Component under app\components\Try\Try.js
'use client'
import axios from 'axios'
import React from 'react'
import useSWR from 'swr'
const Fetcher = url => axios.get(url).then(resp => resp.data.data)
const Try = () => {
const { isLoading, error, data, mutate } = useSWR('/api/tryapi', Fetcher)
if (isLoading) return "Loading...";
else if (error) return <div>
{error.toString()}
<br />
<button onClick={mutate}>Retry</button>
</div>
else {
console.log(data);
return (
<div>
{
data.map((v, i) => (
<p>{v.firstName}</p>
))
}
</div>
)
}
}
export default Try
Output:
return error
in api route.Terminal shows :
Error: No response is returned from route handler 'D:.....\app\api\tryapi\route.js'. Ensure you return a `Response` or a `NextResponse` in all branches of your handler
return NextResponse.json({ data: { error: true } })
Then Retry functionality gets broken.Shows Error in Page:
data.map is not a function
TypeError: data.map is not a function
On Browser Console : Data gets set as below if user clicks on Retry :
SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: PointerEvent, target: button, …}
After Reading this i understood how to handle & send error :
Regarding the error:
SyntheticBaseEvent {_reactName: 'onClick',.....
I changed
<button onClick={mutate}>Retry</button>
to
<button onClick={() => { mutate() }}>Retry</button>
route.js :
I also added axios in route.js instead of fetch, axios directly enters catch when status is in 400s.
let data = await axios.get('https://dummyjson.com/users/1d1ouhl1')
(Yes URL is wrong to purposely get an error msg, from API)
Under catch statement
I added & also changed catch(error)
to catch(e)
(to prevent confusion):
return NextResponse.json("", {
status: e.response.status || 500,
statusText: e.response.code || "Internal Server Error"
})
After doing this error state of useSWR was getting set, Error State from useSWR hook returned (expected behaviour achieved) :
code: "ERR_BAD_RESPONSE"
message: "Request failed with status code 500"
name: "AxiosError"
response :
status: 500
statusText: "Internal Server Error"
data: ""
.....
Explaination : Next.js extends Request and Response Web APIs
Response.json(data, options)