Галоўная / Артыкулы

Дыягностыка дзеянняў сервера Next.js: адказы пра разгароджэнне, CORS і ліміты паданых дадзенняў

Практычныя інструкцыі па адзьвечанню на проблемы, якія поясняюць, чаму функцыі Next.js Server Actions і API-маршруты працююць без супакоментавання або выклікаюць незрозумелыя памылкі, а також прыводзяць конкрэтныя способы ўсунення проблем у кожным з эўтых случаев.

Выдавец: REACTAPP.TOP6 хв чытання1420 слоў

Крыніцы: Common Server Action and API Route Errors in Next.js (and How to Fix Them)

Server Actions значна часткаюць сложнасці ў логіцы бэкенду в Next.js — вам больш не трэба спецыяльнага API-маршрута, каб толькі обрабатваць адправку формы. Але гэта зручнасць супакоўваецца сваімі проблемамі, якія зовсім не супаносяць з багамі, якія з’являліся ў старым Pages Router. Ёсць кантактная інфармацыя пра тое, што часта ламаецца і як з гэтым борцацца.

1. «Памылка: Не удалося знайсці Server Action» пасля аплыву

Усё працюе чыста на вашай машыне, але зламваецца ў той момент, калі выкладзаецца новая версія — корыстнікі пад час сесіі раптам сталквуюцца з гэтай памылкой.

Прычына: Server Actions компілююцца з ID, прыязначанымя да конкрэтнага білда. Якщо хтось мае вікно браузера ачытым пад час вашага аплыву, яго кліент продовжвае выкарыстоўваць ID дзейнасця з старага білда, а сервер іх больш не впішвае.

Рашэнне: нельзя абсалютна запобiec гэтам для пользователяў, які вучасніяць пад час аддачы, але можна правільна распрацаваць гэта падчас, замест таго каб усё проста зламалася без жадных сігналаў:

'use client';
import { useTransition } from 'react';
function SubmitButton({ action }) {
  const [isPending, startTransition] = useTransition();  async function handleClick() {
    try {
      await action();
    } catch (err) {
      if (err.message?.includes('Failed to find Server Action')) {
        window.location.reload(); // force a fresh client with current build IDs
      }
    }
  }  return <button onClick={() => startTransition(handleClick)}>Submit</button>;
}

Захопленне гэтага конкрэтнага бягучага і прабачанне пользователя аб перазавантажэнні дае набліжна лепшы досвядзенне, чым простая, неясная зламка.

2. Дзеяння сервера тыхо не выканана

Вы вызываете дзеянне, ніч не выходзіць, і ніякіх памылак нігде не выдрукавана.

Частая прычына: відсутнае насцярожэнне 'use server' у верхней часты функцыі, або запіс дзеяння ўнутрь Кліентскага Компонента без правильнага пазначэння.

// Wrong — no directive
async function saveUser(formData) {
  await db.user.update(/* ... */);
}

Рашэнне:

'use server';
export async function saveUser(formData) {
  await db.user.update(/* ... */);
}

Якщо дзеянне заданае ў тым жа файле, што і Кліентскі компонент, то дырэктыва павінна знаходзіцца на пачатку самай функцыі, а не проста на пачатку файла:

'use client';
export default function Form() {
  async function saveUser(formData) {
    'use server'; // required here specifically
    await db.user.update(/* ... */);
  }
  return <form action={saveUser}>{/* ... */}</form>;
}

3. „Адчыненне: До Кліентскіх компонентаў можна перадаць толькі простыя об’екты“

Гэта адбываецца, калі значэнне, якое вяртаеся ад Дзеяння сервера, не ўможлівае серыяваць — напрыклад, экземпляр класа, об’ект Date, які знаходзіцца ў незвычайной структуре, або модэль базы дадзенаў, якая мае своія методы.

'use server';
export async function getUser(id) {
  return await db.user.findUnique({ where: { id } }); // ORM might return a class instance
}

Рашэнне — ручнае ператворэнне рэзультата ў просты об’ект:

'use server';
export async function getUser(id) {
  const user = await db.user.findUnique({ where: { id } });
  return {
    id: user.id,
    name: user.name,
    email: user.email,
    createdAt: user.createdAt.toISOString(), // Dates need explicit serialization too
  };
}

Не перадавайце рэзультат ORM безпосередна Кліентскаму компоненту. Спачатку ператворыце яго ў просты, уможлівы для серыяваць об’ект.

4. Дзеяння сервера працюе, але даныя формы вяртаюцца як undefined

<form action={createPost}>
  <input name="title" />
</form>
'use server';
export async function createPost(formData) {
  console.log(formData.title); // undefined
}

Прычына: аргумент formData, які вы отрымляеце, ўособлівае экзамент FormData, а не звычны об’ект JavaScript, таму вам трэба вызваць .get(), каб выявіць з яго значэння.

Рашэнне:

'use server';
export async function createPost(formData) {
  const title = formData.get('title');
  console.log(title); // correct
}

5. "Размер тэла перасілаў прыняць 1 мб" у Server Actions

Server Actions накладаюць стандартны ліміт на размер тэла запыту, што стварае проблему, калі трэба адправляць файлы чыста вялікія пакеты даных з форм.

Рашэнне — явна адбіраць вышэйшы ліміт у файле next.config.js:

// next.config.js
module.exports = {
  experimental: {
    serverActions: {
      bodySizeLimit: '10mb',
    },
  },
};

Для будзь-якага адправлення файлу, якае нагадвае рэальнае адправленне, зазвычай краща вовсе не прабаваць перасылать файл через Server Action. Адправляйте яго безпосередня з кліента у систему хранення об’ектаў — S3, R2 чы іншую — за дапамогою падпісанага URL. Чыглёўна так можна абійсціся з лімітом размеру і не навантажваць сервер.

6. Канцэлекты CORS на маршрутах API, якія вызываюцца з іншага вывару

Гэта проблема з’яўляецца, калі мобільны дапыт, расшырэнне браузера або околічны фронтэнд прабуюць безпосередна запускаць маршруты вашага Next.js API замест таго, каб перайсці через вашы сэрвісы.

Access to fetch at 'https://yourapp.com/api/data' from origin 'https://otherapp.com' has been blocked by CORS policy

Рашэнне — явна задаць заголовкі ў працоўніку маршрута:

// app/api/data/route.js
export async function GET(request) {
  const data = await getData();
  return Response.json(data, {
    headers: {
      'Access-Control-Allow-Origin': 'https://otherapp.com', // never '*' for authenticated routes
      'Access-Control-Allow-Methods': 'GET, POST',
    },
  });
}
export async function OPTIONS() {
  return new Response(null, {
    headers: {
      'Access-Control-Allow-Origin': 'https://otherapp.com',
      'Access-Control-Allow-Methods': 'GET, POST',
    },
  });
}

Хаця б утримвацца ад задання Access-Control-Allow-Origin: '*' на будзь-ям канцэнтры, якая выкорыстоўвае кукуі аутантыкацыі. Такое рашэнне фактычна дае даныя корыстніка таму вывару, які толькі прагучае ўзяць іх.

7. Працоўнік маршрута вяртае HTML замест JSON у разы аберты

Калі працоўнік маршрута выклікае неконтрольваную аберту, кліент не отрымае структураванага JSON-звесткі пра аберту — у замен ён отрымае вбудованую стороннюю сторунку аберты Next.js. Гэта прычына проблем для будзь-якога кліентскага коду, який вырачоўвае, што можа безпечна вызываць response.json().

Рашэнне — абгуліце логіку обробніка ў try/catch, які завжды адправляе JSON:

export async function POST(request) {
  try {
    const body = await request.json();
    const result = await processOrder(body);
    return Response.json(result);
  } catch (error) {
    return Response.json(
      { error: error.message ?? 'Something went wrong' },
      { status: 500 }
    );
  }
}

За такім падходам кліент завжды атрымлее тое, што можна распакаваць як JSON, незалежна ад таго, чы спрашчэк успелі чы ня, замест аддокумента HTML, які спануляе вызов .json() пазней у кодзе.

Чаму такія адказы здаюцца горшымі, чым багі старых API-маршрутаў

Дзеянні сервера спаўнаюць логіку кліента і сервера ў аднам файле, што робіць нейкак не простым перасягнуць межы без адчутка. Значэнне, яке вяртаецца, можа выглядаць абсалютна нормальным у вашам редактары, але ўсё равно спрацаваць некоректна пад час запуску, таму што яго нельга серыялізаваць; або ж відсутняй дырэктыва можа спрычыніць проблему тыхамо, замест таго каб выклікана была падзея там, дзе яна мала б выйсці. Звычныя методы адлучэння прычын бяга коду для API у стылі REST — адкрыванне вікна сеті, перагляд адпаведзі — не завжды дапамагаюць у такіх ситуацыях, адтуды што калькі з такімі проблемамі выйшлі ўжо до таго, як быў адправлены хоць які-небудзь сетавы запит.

Для команд, які ствараюць панелі керування, заполненыя формамі, завантажэнням файлоў і змěнамі, распадзеленымі па многіх сторунках, варта ўжыць гэтыя падходы — серіялізацыю, карэткаванне памылак, ліміты розмеру тэла — і ўсё гэта рашыць адночасна ў всім кодбазе, замест таго кожны раз перерашыць тыя ж проблемы ў кожной новай форме. Дзеякія готовыя шаблоны адуправлення для SaaS-продуктав, якія выкарыстоўваюць AI, вже маюць правільна налаштаваныя Server Actions і API-маршруты, укладаныя з падтрымкай верыфікацыі за дапамогою Zod і належным карэткаванням памылак з самага початку, таму гэтыя семь проблем рашаюцца толькі раз, а не з’яўляюцца знову ў кожной форме, яку вы ствараеце.

Узагальненне

Дзеянні сервера скарматываюць значную частку стандартнага коду, які ранейшыя API-маршруты выкарыстоўвалі, але яны прыносяць з сабой сваю сяброўку способаў абвалення: ID-ы будов выцікаюць пасля развяртання, нехапячыце інструкцый, значэнні, якія нельга серыялізаваць, неправильнае выкарыстоўванне FormData, ліміты розмеру тэла, абваленні CORS межы джэронаў, а таксама адпаведзі на падазры, якія на самай працоўцы не ёсць JSON. Усе сям абвалення часта з’яўляюцца, усе сям можна вылечыць за калькі ліній коду, і усе сям заслуговуюць на шыраную перапрацоўку пры тым, калі вы прыпускаеце, што ўсё гэта складнейшая проблема.