$ cat contact-form-resend-route-handlers.mdx
NEXT.JS
ការសាងសង់ Contact Form ដែលធន់នឹង Spam ជាមួយនឹង Next.js Route Handlers និងវេទិកា Resend
ក្នុងអត្ថបទនេះ
ការសាងសង់ Contact Form ដែលធន់នឹង Spam
រាល់ portfolio តែងតែត្រូវការ contact form ហើយការណែនាំជាច្រើនច្រើនតែផ្តោតទៅលើ hosted service ដូចជា Formspree។ វាដំណើរការបានល្អ ប៉ុន្តែវាមានន័យថាភាគីទីបីនៅចន្លោះអ្នកចូលមើលរបស់អ្នក និងប្រអប់សំបុត្ររបស់អ្នក, កម្រិតចូលមើលដោយឥតគិតថ្លៃមានកំណត់ ហើយអ្នកមិនអាចប្ដូរតាមបំណងសម្រាប់ការផ្ទៀងផ្ទាត់ទិន្នន័យ (validation) ឬការចាត់ចែង spam ទេ។
ដោយសារតែវែបសាយនេះដំណើរការដោយប្រើប្រាស់ Next.js ខ្ញុំបានផ្លាស់ប្តូរផ្នែកនេះទាំងស្រុងទៅក្នុង Route Handler។ នេះគឺជារបៀបដែលវាដំណើរការ។
ផ្នែក API route
Route Handlers មានទីតាំងនៅក្នុង directory app ជាឯកសារ route.ts។ Route របស់ខ្ញុំស្ថិតនៅក្នុង app/api/contact/route.ts ហើយ export តែ POST function មួយប៉ុណ្ណោះ:
export async function POST(request: Request) {
const payload = await request.json();
const name = typeof payload.name === "string" ? payload.name.trim() : "";
const email = typeof payload.email === "string" ? payload.email.trim() : "";
const message =
typeof payload.message === "string" ? payload.message.trim() : "";
// ...validate, then send
}សូមចំណាំពីការ parsing ប្រុងប្រយ័ត្ន: ខ្ញុំមិនដែលសន្មតថាវាជា string គ្រាន់តែដោយសារតែ frontend បញ្ជូនវាទេ។ នរណាម្នាក់អាច curl ទៅ endpoint របស់អ្នកជាមួយនឹង payload ដែលពួកគេចង់បាន។
ការផ្ទៀងផ្ទាត់នៅលើ server មិនមែនតែលើ browser ទេ
Input របស់ form មាន attributes required និង type="email" ប៉ុន្តែការផ្ទៀងផ្ទាត់លើ browser គឺគ្រាន់តែជួយសម្រួលប៉ុណ្ណោះ មិនមែនជាការធានាទេ។ Route ធ្វើការត្រួតពិនិត្យឡើងវិញទាំងអស់:
if (!name || name.length > 100) {
return NextResponse.json(
{ error: "សូមបញ្ចូលឈ្មោះរបស់អ្នក។" },
{ status: 400 },
);
}
if (!EMAIL_REGEX.test(email) || email.length > 254) {
return NextResponse.json(
{ error: "សូមបញ្ចូលអាសយដ្ឋានអ៊ីមែលដែលមានសុពលភាព។" },
{ status: 400 },
);
}ការដាក់ការកំណត់ទំហំគឺមានសារៈសំខាន់ជាងអ្វីដែលមើលឃើញទៅទៀត។ បើគ្មានវាទេ នរណាម្នាក់អាច POST នូវ "សារ" ទំហំ 10 MB ហើយអ្នកនឹងបញ្ជូនវាចូលប្រអប់សំបុត្ររបស់អ្នកយ៉ាងសប្បាយ។
នុយទាក់បត (Honeypot): ថោក និងមានប្រសិទ្ធភាពខ្លាំង
Spam bots នឹងបំពេញរាល់ field ដែលពួកវាឃើញ។ អ្នកប្រើប្រាស់ពិតប្រាកដមិនដែលឃើញ fields ដែលលាក់នោះទេ។ ដូច្នេះ form នេះរួមបញ្ចូលទាំង input មួយដែលមនុស្សមើលមិនឃើញ:
<input
type="text"
name="company"
tabIndex={-1}
autoComplete="off"
aria-hidden="true"
className="hidden"
/>ប្រសិនបើ company ត្រូវបានផ្ញើមកជាមួយតម្លៃ នោះ API នឹងបង្វិលសងនូវ ភាពជោគជ័យក្លែងក្លាយ ហើយដោយស្ងាត់ស្ងៀមទម្លាក់សារចោល:
if (honeypot) {
return NextResponse.json({ ok: true });
}ការបង្វិលសងភាពជោគជ័យជំនួសឱ្យបញ្ហាគឺជាចេតនា - អ្នកមិនចង់បង្រៀនបតឱ្យដឹងថាតើសំណើណាមួយរបស់វាត្រូវបានច្រានចោលនោះទេ។
ការផ្ញើអ៊ីមែលជាមួយនឹង Resend
Resend មាន API ងាយស្រួល ហើយមានផ្ទៃឥតគិតថ្លៃគ្រប់គ្រាន់សម្រាប់ portfolio មួយ។ ល្បិចមួយដែលអ្នកគួរដឹងគឺ replyTo:
const resend = new Resend(process.env.RESEND_API_KEY);
await resend.emails.send({
from: "Portfolio Contact <onboarding@resend.dev>",
to: siteConfig.email,
replyTo: email, // ចុច "Reply" នោះអ្នកនឹងឆ្លើយតបទៅអ្នកចូលទស្សនាដោយផ្ទាល់
subject: `Portfolio contact ពី ${name}`,
text: `Name: ${name}\nEmail: ${email}\n\n${message}`,
});អ៊ីមែលមកដល់ពី sending domain របស់អ្នក ប៉ុន្តែការឆ្លើយតបវិញចូលទៅកាន់អ្នកចូលទស្សនាត្រង់តែម្តង - មិនបាច់ copy-pasting អាសយដ្ឋានឡើយ។
ខ្ញុំក៏ផ្ញើជា plain text ជាជាងប្រើប្រាស់ HTML ផងដែរ។ Content នៅក្នុង form គឺជាអ្វីដែលអ្នកប្រើប្រាស់វាយបញ្ចូល; ការបញ្ចូលវាទៅក្នុង HTML គឺមានន័យថាអ្នកត្រូវសម្អាតវាជាមុនសិន។ Plain text គឺបំបាត់បញ្ហានេះទាំងស្រុង។
Frontend: បង្ហាញពីសារកំហុសរបស់ server
ដោយសារតែ API ត្រលប់មកវិញជាមួយសារកំហុសជាក់ច្បាស់ form នឹងបង្ហាញពួកវាជំនួសឱ្យអត្ថបទ "មានអ្វីមួយមិនត្រឹមត្រូវ" ជាទូទៅ:
if (response.ok) {
setStatus("success");
} else {
const body = await response.json().catch(() => null);
setErrorMessage(body?.error ?? null);
setStatus("error");
}បច្ចេកទេស .catch(() => null) ការពារកុំឱ្យមានផលប៉ះពាល់ប្រសិនបើមានការចាប់ខុស (non-JSON error response) (server គាំងត្រឡប់ជា HTML) — ដែលអាចជួយរារាំងមិនឱ្យការបដិសេធគ្មានអ្នកដោះស្រាយកើតមានឡើង នៅពេលដែលអ្វីៗកំពុងតែខូចខាត។
ចំណុចសំខាន់ៗដែលទទួលបាន
- Route Handlers ធ្វើអោយ third-party form services ក្លាយជាជម្រើសមួយ។ វាមានប្រហែលតែ៧០បន្ទាត់ប៉ុណ្ណោះ។
- ផ្ទៀងផ្ទាត់លើ server ជាមួយនឹងការកំណត់ទំហំ មិនមែនតែលើ browser ទេ។
- A honeypot field ជួយច្រានចោលសារ spam ដោយឥតគិតថ្លៃ យ៉ាងស្ងៀមស្ងាត់។
replyToបម្លែង transactional email ទៅកាន់ប្រអប់សារដែលអាចឆ្លើយទៅបានយ៉ាងងាយ។
ចំណាយសរុប: អថេរ environment (environment variable) មួយ និងថ្លៃសេវាសូន្យរៀងរាល់ខែ។