最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to throw and handle custom errors from server to client side in Next.js - Stack Overflow

programmeradmin5浏览0评论

I'm building a Next.js application and I'm trying to send custom error messages from the server to the client side when using Next JS new server-side actions. My server-side code is working fine, but I'm having trouble handling custom error types. When I try to catch these errors on the client side, I receive the following error: "Warning: Only plain objects can be passed to Client Components from Server Components. Error objects are not supported."

Here's my client-side code:

"use client"
async function handleDelete() {
    try {
        await deleteAddress(contactId, addressId)
    } catch (e) {
        console.log(e instanceof FetchError) // Is always false
    }
}

And here's my server-side code:

"use server"
async function deleteAddress(contactId: number, addressId: number) {
    const response = await fetch(process.env.BASE_API_URL + "/contacts/" + contactId + "/addresses/" + addressId, {
        cache: "no-cache",
        method: "DELETE"
    });

    if (!response.ok) {
        throw new FetchError("There was a problem with your request", "Something went wrong", "destructive");
    }
}

My goal is to catch the FetchError on the client side and handle it appropriately. What is the best way to send custom errors back to the client side in Next.js while avoiding the "Error objects are not supported" issue?

I'm building a Next.js application and I'm trying to send custom error messages from the server to the client side when using Next JS new server-side actions. My server-side code is working fine, but I'm having trouble handling custom error types. When I try to catch these errors on the client side, I receive the following error: "Warning: Only plain objects can be passed to Client Components from Server Components. Error objects are not supported."

Here's my client-side code:

"use client"
async function handleDelete() {
    try {
        await deleteAddress(contactId, addressId)
    } catch (e) {
        console.log(e instanceof FetchError) // Is always false
    }
}

And here's my server-side code:

"use server"
async function deleteAddress(contactId: number, addressId: number) {
    const response = await fetch(process.env.BASE_API_URL + "/contacts/" + contactId + "/addresses/" + addressId, {
        cache: "no-cache",
        method: "DELETE"
    });

    if (!response.ok) {
        throw new FetchError("There was a problem with your request", "Something went wrong", "destructive");
    }
}

My goal is to catch the FetchError on the client side and handle it appropriately. What is the best way to send custom errors back to the client side in Next.js while avoiding the "Error objects are not supported" issue?

Share Improve this question asked Oct 21, 2023 at 15:11 hantorenhantoren 1,2756 gold badges27 silver badges47 bronze badges 1
  • do yo have solution on this one? we have literally the same problem. – aRtoo Commented May 8, 2024 at 5:21
Add a ment  | 

3 Answers 3

Reset to default 4

You should serialize the error message into a plain object and then send it to the client.

On the server side (in your server ponent), create a custom error object and serialize it into a plain object with a message that you can send to the client.

// serverComponent.ts
export async function serverFunction() {
  try {
    // Your server-side code here
    // If an error occurs, create a custom error object
    throw new Error("Custom error message");
  } catch (error) {
    // Serialize the error into a plain object
    return { error: { message: error.message } };
  }
}

On the client side (in your client ponent), check for the error object and handle it accordingly.

// clientComponent.tsx
import { serverFunction } from './serverComponent';

export default function ClientComponent({ error }) {
  if (error) {
    // Handle the error here, such as displaying it to the user
    return <div>Error: {error.message}</div>;
  }

  // Render the ponent content when there are no errors
  return <div>Your ponent content here</div>;
}

export async function getServerSideProps() {
  // Call the server ponent function and pass any error object
  const result = await serverFunction();
  
  return {
    props: {
      error: result.error, // Pass the error object to the client ponent
    },
  };
}

As I have learned it recently, Error Boundaries do not catch errors inside event handlers. Your handleDelete is triggered by an event I presume.

As Radmehr wrote, one way is to serialize the error into a new object (for example return { error: e.message }) inside the server action and return that object from the same catch block to the client. It still won't trigger a client side catch, you need to throw a new Error with the message returned as response by the action.

Another solution I currently use for this scenario is the following (feels a bit hacky but works fine with the App router):

  1. on the client side, set up a state variable for error messages
  2. still on the client side, write a useEffect to watch for the changes of the error state
  3. inside the server action, throw a new Error (as you did should be fine)
  4. on the client side, wrap your server action call in a try-catch (also same as you did)
  5. now in the catch block, set the error instance's message property as the state
  6. as soon as the state changes, the useEffect runs and throws a new Error which can be caught by the nextjs error boundary :)

NOTE: you'll get an uncaught error warning in the browser but it's safe to ignore.

plete client side example:

"use client"

const [error, setError] = useState('');

useEffect(() => {
  if (error) throw error;
}, [error]);

async function handleDelete() {
  try {
    await deleteAddress(contactId, addressId)
  } catch (e) {
   const e = error as Error;
   setError(e.message);
  }
}
 class ErrorHandler extends Error {
    constructor(message, statusCode) {
        super(message);
        this.statusCode = statusCode;
        Error.captureStackTrace(this, this.constructor);
    }

    sendErrorResponse(res) {
        return res.status(this.statusCode).json({
            success: false,
            error: this.message,
            message: this.message,
        });
    }
}    
module.exports = ErrorHandler;


const ErrorHandler = require("../utils/errorHandler");
//like 
if (!response.ok) {
 return new ErrorHandler('Something went wrong', 400).sendErrorResponse(res);
 }
发布评论

评论列表(0)

  1. 暂无评论