Integracja z backendem formularza Next.js (akcje routera aplikacji i serwera)
Kompletny przewodnik implementacji TypeScript i React dotyczący obsługi formularzy kontaktowych w Next.js 14/15 przy użyciu punktów końcowych Lugatu i akcji serwera bez ujawniania poświadczeń.
1. Architektura routera aplikacji Next.js z akcjami serwera
Akcje serwera Next.js są wykonywane wyłącznie na serwerze, co czyni je idealnymi do przesyłania zapytań z prywatnymi kluczami Lugatu API bez ujawniania tajemnic w pakietach klienta.
'use server';
export async function submitContact(prevState: any, formData: FormData) {
// Silent rejection of automated bot submissions via honeypot trap
const honeypot = (formData.get('_gotcha') as string) || '';
if (honeypot) {
return { success: true, error: null };
}
const name = formData.get('name') as string;
const email = formData.get('email') as string;
const message = formData.get('message') as string;
if (!name || !email || !message) {
return { success: false, error: 'All fields are required.' };
}
const payload = {
fields: { name, email, message },
};
try {
const res = await fetch(`https://www.lugatu.com/api/v1/forms/${process.env.LUGATU_FORM_ID}/submissions`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LUGATU_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': crypto.randomUUID(),
},
body: JSON.stringify(payload),
});
if (!res.ok) {
return { success: false, error: 'Failed to submit form.' };
}
return { success: true, error: null };
} catch (err) {
return { success: false, error: 'Network error occurred.' };
}
}
2. Komponent formularza po stronie klienta ze stanem ładowania
Połącz swoją akcję serwera z komponentem klienta React 19 / Next.js z dostępnymi wskaźnikami ładowania.
'use client';
import { useActionState } from 'react';
import { submitContact } from '@/app/actions/contact';
export function ContactForm() {
const [state, formAction, isPending] = useActionState(submitContact, { success: false, error: null });
return (
<form action={formAction} className="space-y-4 max-w-md">
<div>
<label className="block text-sm font-semibold mb-1">Name</label>
<input name="name" required className="w-full border rounded-xl p-2.5" />
</div>
<div>
<label className="block text-sm font-semibold mb-1">Email</label>
<input type="email" name="email" required className="w-full border rounded-xl p-2.5" />
</div>
<div>
<label className="block text-sm font-semibold mb-1">Message</label>
<textarea name="message" rows={4} required className="w-full border rounded-xl p-2.5" />
</div>
<input type="text" name="_gotcha" style={{ display: 'none' }} tabIndex={-1} autoComplete="off" />
<button type="submit" disabled={isPending} className="btn-primary-editorial w-full py-2.5">
{isPending ? 'Sending...' : 'Send Message'}
</button>
{state.success && <p className="text-emerald-700 text-sm">Thank you! Your message was received.</p>}
{state.error && <p className="text-red-700 text-sm">{state.error}</p>}
</form>
);
}
Niezmiennik bezpieczeństwa i uwierzytelniania
Instrukcja bezpieczeństwa: Nigdy nie poprzedzaj LUGATU_API_KEY prefiksem NEXT_PUBLIC_. Tylko prywatne zmienne po stronie serwera mogą bezpiecznie przechowywać Twój aktywny klucz API.
Rozwiązywanie problemów i typowe błędy przetwarzania
Wykonujesz wywołanie fetch() z kodu przeglądarki po stronie klienta za pomocą klucza API. Przejdź na akcję serwerową Next.js lub użyj punktu końcowego formularza publicznego bez klucza API.
Często zadawane pytania
Czy mogę wdrożyć to na stronach Vercel lub Cloudflare?
Tak. Akcje serwera działają bezproblemowo na platformach bezserwerowych i środowiskach wykonawczych brzegowych obsługiwanych przez Next.js.
Gotowy do utworzenia punktu końcowego formularza?
Rozpocznij bezpłatnie w ciągu kilku sekund. Nie jest wymagana karta kredytowa.