How to handle errors in NextJS 13 API Routes while using useSWR hook?

943 views Asked by At

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:

  • After passing wrong url & returning 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
  • If return I response 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, …}
1

There are 1 answers

0
Beast80K On BEST ANSWER

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)
  • body, options both are optional
  • While sending error i kept body part empty, & just set status due to this data recieved in error object is "" empty. (which i prefer to keep empty as its none of use)