next.jsnext.js13swr

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


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:

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

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, …}

Solution

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