Як правильно хендлити помилки з Next.js Server Actions на клієнті (і не зійти з розуму)

💡 Усі статті, обговорення, новини про Front-end — в одному місці. Приєднуйтесь до Front-end спільноти!

Помилки — це як діти: якщо їх не виховувати, вони виростають і починають псувати життя.

Вступ

Next.js Server Actions — це крута штука, яка дозволяє виконувати серверний код прямо з клієнта. Але, як і в кожній великій родині, є свої «діти» — і це помилки, які можуть зіпсувати настрій. Особливо коли в production Next.js замовчує конкретні повідомлення про помилки, залишаючи вас з розмитим Error: An error occurred.

Якщо ви вже ламали голову над тим, як передавати та обробляти помилки нормально — ця стаття для вас. Ми розглянемо, як безпечно прокинути помилку з серверу на клієнт (і не отримати депресію у процесі).

Проблема: Чому Next.js приховує помилки?

Припустимо, у вас є ось такий Server Action:

На клієнті ви викликаєте цю функцію:

Очікуємо побачити Некоректний email!? Ага! У production Next.js поверне щось схоже на: Error: An error occurred

WTF?!

Спойлер: Next.js навмисно приховує деталі помилки у production режимі з міркувань безпеки.

Щоб передавати помилки коректно (і щоб Next.js їх не ховав), ми можемо використати кастомний формат помилок.

1. Створюємо кастомний тип помилки

2. Модифікуємо Server Action

Замість того, щоб просто кидати throw new Error(), повертаємо об’єкт з error:

3. Пишемо хелпер для хендлінгу помилок

Тепер треба перехоплювати помилки на клієнті:

isServerActionError — Type Guard Function яка перевіряє, чи є відповідь від серверного екшену помилкою.

handleServerAction — функція-обгортка, яка викликає серверний екшен і викидає помилку, якщо вона є.

4. Обробляємо відповідь на клієнті

Тепер, навіть у production, ми бачимо конкретні помилки замість An error occurred.

Висновок

— Next.js ховає деталі помилки в production, але це можна обійти через кастомний формат помилок.
— Замість throw new Error() повертаємо об’єкт { error: '...' }.
— На клієнті handleServerAction викликає серверний екшн і викидає помилку, якщо вона є.

Все! Тепер ви більше не будете питати себе: «Чому я не бачу нормальних помилок?!» 😎

An Error Occurred! : r/lethalcompany

👍ПодобаєтьсяСподобалось1
До обраногоВ обраному0
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

nextjs.org/...​-mutations#error-handling — в доке кажуть юзати ось такий підхід

'use server'
 
import { redirect } from 'next/navigation'
 
export async function createUser(prevState: any, formData: FormData) {
  const res = await fetch('https://...')
  const json = await res.json()
 
  if (!res.ok) {
    return { message: 'Please enter a valid email' }
  }
 
  redirect('/dashboard')
}
'use client'
 
import { useActionState } from 'react'
import { createUser } from '@/app/actions'
 
const initialState = {
  message: '',
}
 
export function Signup() {
  const [state, formAction, pending] = useActionState(createUser, initialState)
 
  return (
    <form action={formAction}>
      <label htmlFor="email">Email</label>
      <input type="text" id="email" name="email" required />
      {/* ... */}
      <p aria-live="polite">{state?.message}</p>
      <button disabled={pending}>Sign up</button>
    </form>
  )
}

Так, в документації описаний такий варіант.
Проте наведений в статті підхід відповідає традиційному способу обробки асинхронних функцій, особливо коли потрібно виконати кілька таких функцій послідовно як приклад:

async function handleMultipleActions() {
    try {
        const user = await handleServerAction(createUser({ email: '[email protected]' }));
        const profile = await handleServerAction(createUserProfile({ userId: user.id }));
    } catch (error) {
        printError(error);
    }
}

Підписатись на коментарі