Poznaj trasy API Next.js i odblokuj możliwości programowania full-stack w swoich aplikacjach React. Ucz się wzorców, najlepszych praktyk i strategii wdrażania.
Trasy API Next.js: Wzorce programowania Full-Stack
Next.js zrewolucjonizował tworzenie aplikacji React, zapewniając solidny framework do budowania wydajnych i skalowalnych aplikacji internetowych. Jedną z jego kluczowych cech są trasy API, które umożliwiają programistom tworzenie funkcjonalności backendu bezpośrednio w projektach Next.js. Takie podejście usprawnia proces tworzenia, upraszcza wdrażanie i odblokowuje potężne możliwości full-stack.
Co to są trasy API Next.js?
Trasy API Next.js to funkcje bezserwerowe pisane bezpośrednio w katalogu /pages/api
. Każdy plik w tym katalogu staje się punktem końcowym API, automatycznie kierując żądania HTTP do odpowiadającej mu funkcji. Eliminuje to potrzebę oddzielnego serwera backendu, upraszczając architekturę aplikacji i redukując koszty operacyjne.
Pomyśl o nich jak o miniaturowych funkcjach bezserwerowych, które znajdują się w twojej aplikacji Next.js. Odpowiadają na żądania HTTP, takie jak GET, POST, PUT, DELETE i mogą wchodzić w interakcje z bazami danych, zewnętrznymi interfejsami API i innymi zasobami po stronie serwera. Co ważne, działają tylko na serwerze, a nie w przeglądarce użytkownika, zapewniając bezpieczeństwo poufnych danych, takich jak klucze API.
Kluczowe korzyści tras API
- Uproszczone tworzenie: Pisanie kodu frontend i backend w tym samym projekcie.
- Architektura bezserwerowa: Wykorzystanie funkcji bezserwerowych dla skalowalności i efektywności kosztowej.
- Łatwe wdrażanie: Wdrażanie frontendu i backendu razem za pomocą jednego polecenia.
- Poprawiona wydajność: Renderowanie po stronie serwera i możliwości pobierania danych zwiększają szybkość aplikacji.
- Zwiększone bezpieczeństwo: Poufne dane pozostają na serwerze, chronione przed ekspozycją po stronie klienta.
Rozpoczęcie pracy z trasami API
Tworzenie trasy API w Next.js jest proste. Po prostu utwórz nowy plik w katalogu /pages/api
. Nazwa pliku określi ścieżkę trasy. Na przykład, utworzenie pliku o nazwie /pages/api/hello.js
spowoduje utworzenie punktu końcowego API dostępnego pod adresem /api/hello
.
Przykład: Prosty interfejs API powitania
Oto podstawowy przykład trasy API, która zwraca odpowiedź JSON:
// pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: 'Witaj z trasy API Next.js!' });
}
Ten kod definiuje asynchroniczną funkcję handler
, która otrzymuje dwa argumenty:
req
: Instancjęhttp.IncomingMessage
, plus niektóre wstępnie wbudowane elementy pośredniczące.res
: Instancjęhttp.ServerResponse
, plus niektóre funkcje pomocnicze.
Funkcja ustawia kod statusu HTTP na 200 (OK) i zwraca odpowiedź JSON z komunikatem.
Obsługa różnych metod HTTP
Możesz obsługiwać różne metody HTTP (GET, POST, PUT, DELETE itp.) w swojej trasie API, sprawdzając właściwość req.method
. Umożliwia to łatwe tworzenie interfejsów API RESTful.
// pages/api/todos.js
export default async function handler(req, res) {
if (req.method === 'GET') {
// Pobierz wszystkie zadania z bazy danych
const todos = await fetchTodos();
res.status(200).json(todos);
} else if (req.method === 'POST') {
// Utwórz nowe zadanie
const newTodo = await createTodo(req.body);
res.status(201).json(newTodo);
} else {
// Obsługa nieobsługiwanych metod
res.status(405).json({ message: 'Metoda niedozwolona' });
}
}
Ten przykład pokazuje, jak obsługiwać żądania GET i POST dla hipotetycznego punktu końcowego /api/todos
. Zawiera również obsługę błędów dla nieobsługiwanych metod.
Wzorce programowania Full-Stack z trasami API
Trasy API Next.js umożliwiają różne wzorce programowania full-stack. Oto kilka typowych przypadków użycia:
1. Pobieranie i manipulowanie danymi
Trasy API mogą być używane do pobierania danych z baz danych, zewnętrznych interfejsów API lub innych źródeł danych. Można ich również używać do manipulowania danymi, np. tworzenia, aktualizacji lub usuwania rekordów.
Przykład: Pobieranie danych użytkownika z bazy danych
// pages/api/users/[id].js
import { query } from '../../../lib/db';
export default async function handler(req, res) {
const { id } = req.query;
try {
const results = await query(
'SELECT * FROM users WHERE id = ?',
[id]
);
if (results.length === 0) {
return res.status(404).json({ message: 'Użytkownik nie znaleziony' });
}
res.status(200).json(results[0]);
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Błąd wewnętrzny serwera' });
}
}
Ten przykład pobiera dane użytkownika z bazy danych na podstawie identyfikatora użytkownika podanego w adresie URL. Używa biblioteki zapytań do bazy danych (zakłada się, że znajduje się w lib/db
) do interakcji z bazą danych. Zwróć uwagę na użycie parametryzowanych zapytań, aby zapobiec lukom w zabezpieczeniach typu SQL injection.
2. Uwierzytelnianie i autoryzacja
Trasy API mogą być używane do implementacji logiki uwierzytelniania i autoryzacji. Możesz ich używać do weryfikacji danych uwierzytelniających użytkownika, generowania tokenów JWT i ochrony poufnych zasobów.
Przykład: Uwierzytelnianie użytkownika
// pages/api/login.js
import bcrypt from 'bcryptjs';
import jwt from 'jsonwebtoken';
import { query } from '../../lib/db';
export default async function handler(req, res) {
if (req.method === 'POST') {
const { email, password } = req.body;
try {
const results = await query(
'SELECT * FROM users WHERE email = ?',
[email]
);
if (results.length === 0) {
return res.status(401).json({ message: 'Nieprawidłowe dane uwierzytelniające' });
}
const user = results[0];
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ message: 'Nieprawidłowe dane uwierzytelniające' });
}
const token = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.status(200).json({ token });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Błąd wewnętrzny serwera' });
}
} else {
res.status(405).json({ message: 'Metoda niedozwolona' });
}
}
Ten przykład uwierzytelnia użytkowników, porównując podane hasło z przechowywanym hasłem zhaszowanym w bazie danych. Jeśli dane uwierzytelniające są prawidłowe, generuje token JWT i zwraca go do klienta. Klient może następnie użyć tego tokena do uwierzytelniania kolejnych żądań.
3. Obsługa formularzy i przesyłanie danych
Trasy API mogą być używane do obsługi przesyłania formularzy i przetwarzania danych wysyłanych od klienta. Jest to przydatne do tworzenia formularzy kontaktowych, formularzy rejestracyjnych i innych elementów interaktywnych.
Przykład: Przesyłanie formularza kontaktowego
// pages/api/contact.js
import { sendEmail } from '../../lib/email';
export default async function handler(req, res) {
if (req.method === 'POST') {
const { name, email, message } = req.body;
try {
await sendEmail({
to: 'admin@example.com',
subject: 'Nowe zgłoszenie z formularza kontaktowego',
text: `Imię: ${name}\nEmail: ${email}\nWiadomość: ${message}`,
});
res.status(200).json({ message: 'E-mail wysłany pomyślnie' });
} catch (error) {
console.error(error);
res.status(500).json({ message: 'Nie udało się wysłać e-maila' });
}
} else {
res.status(405).json({ message: 'Metoda niedozwolona' });
}
}
Ten przykład obsługuje zgłoszenie formularza kontaktowego, wysyłając e-mail do administratora. Używa biblioteki wysyłania e-maili (zakłada się, że znajduje się w lib/email
), aby wysłać e-mail. Powinieneś zastąpić admin@example.com
rzeczywistym adresem e-mail odbiorcy.
4. Webhooki i obsługa zdarzeń
Trasy API mogą być używane do obsługi webhooków i reagowania na zdarzenia z usług zewnętrznych. Umożliwia to integrację aplikacji Next.js z innymi platformami i automatyzację zadań.
Przykład: Obsługa webhooka Stripe
// pages/api/stripe-webhook.js
import Stripe from 'stripe';
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
export const config = {
api: {
bodyParser: false, // Wyłącz domyślne parsowanie treści
},
};
async function buffer(req) {
const chunks = [];
for await (const chunk of req) {
chunks.push(chunk);
}
return Buffer.concat(chunks).toString();
}
export default async function handler(req, res) {
if (req.method === 'POST') {
const sig = req.headers['stripe-signature'];
let event;
try {
const buf = await buffer(req);
event = stripe.webhooks.constructEvent(buf, sig, process.env.STRIPE_WEBHOOK_SECRET);
} catch (err) {
console.log(`Błąd webhooka: ${err.message}`);
res.status(400).send(`Błąd webhooka: ${err.message}`);
return;
}
// Obsługa zdarzenia
switch (event.type) {
case 'payment_intent.succeeded':
const paymentIntent = event.data.object;
console.log(`PaymentIntent dla ${paymentIntent.amount} zakończony sukcesem!`);
// Następnie zdefiniuj i wywołaj metodę obsługującą pomyślne uruchomienie payment intent.
// handlePaymentIntentSucceeded(paymentIntent);
break;
case 'payment_method.attached':
const paymentMethod = event.data.object;
// Następnie zdefiniuj i wywołaj metodę obsługującą pomyślne dołączenie PaymentMethod.
// handlePaymentMethodAttached(paymentMethod);
break;
default:
// Niespodziewany typ zdarzenia
console.log(`Nierozpoznany typ zdarzenia ${event.type}.`);
}
// Zwróć odpowiedź 200, aby potwierdzić odbiór zdarzenia
res.status(200).json({ received: true });
} else {
res.setHeader('Allow', 'POST');
res.status(405).end('Metoda niedozwolona');
}
}
Ten przykład obsługuje webhooka Stripe, weryfikując podpis i przetwarzając dane zdarzenia. Wyłącza domyślny parser treści i używa niestandardowej funkcji bufora do odczytu nieprzetworzonej treści żądania. Konieczne jest wyłączenie domyślnego parsera treści, ponieważ Stripe wymaga nieprzetworzonej treści do weryfikacji podpisu. Pamiętaj, aby skonfigurować punkt końcowy webhooka Stripe w swoim panelu Stripe i ustawić zmienną środowiskową STRIPE_WEBHOOK_SECRET
.
Najlepsze praktyki dotyczące tras API
Aby zapewnić jakość i łatwość konserwacji tras API, przestrzegaj następujących najlepszych praktyk:
1. Modularizuj swój kod
Unikaj pisania dużych, monolitowych tras API. Zamiast tego podziel swój kod na mniejsze, wielokrotnego użytku moduły. Ułatwia to zrozumienie, testowanie i konserwację kodu.
2. Implementacja obsługi błędów
Prawidłowo obsługuj błędy w swoich trasach API. Użyj bloków try...catch
do przechwytywania wyjątków i zwracania odpowiednich odpowiedzi o błędach do klienta. Rejestruj błędy, aby ułatwić debugowanie i monitorowanie.
3. Walidacja danych wejściowych
Zawsze sprawdzaj poprawność danych wejściowych od klienta, aby zapobiec lukom w zabezpieczeniach i zapewnić integralność danych. Użyj bibliotek walidacji, takich jak Joi lub Yup, aby zdefiniować schematy walidacji i wymusić ograniczenia danych.
4. Chroń poufne dane
Przechowuj poufne dane, takie jak klucze API i dane uwierzytelniające bazy danych, w zmiennych środowiskowych. Nigdy nie zatwierdzaj poufnych danych do repozytorium kodu.
5. Implementacja ograniczania liczby żądań
Chroń swoje trasy API przed nadużyciami, implementując ograniczanie liczby żądań. Ogranicza to liczbę żądań, które klient może wykonać w danym okresie czasu. Użyj bibliotek ograniczania liczby żądań, takich jak express-rate-limit
lub limiter
.
6. Zabezpiecz klucze API
Nie ujawniaj kluczy API bezpośrednio w kodzie po stronie klienta. Zawsze kieruj żądania przez swoje trasy API, aby chronić swoje klucze API przed nieautoryzowanym dostępem. Przechowuj klucze API bezpiecznie w zmiennych środowiskowych na swoim serwerze.
7. Użyj zmiennych środowiskowych
Unikaj kodowania na stałe wartości konfiguracji w swoim kodzie. Zamiast tego użyj zmiennych środowiskowych do przechowywania ustawień konfiguracji. Ułatwia to zarządzanie aplikacją w różnych środowiskach (tworzenie, etap, produkcja).
8. Rejestrowanie i monitorowanie
Zaimplementuj rejestrowanie i monitorowanie, aby śledzić wydajność swoich tras API. Rejestruj ważne zdarzenia, takie jak błędy, ostrzeżenia i pomyślne żądania. Użyj narzędzi monitorowania, aby śledzić metryki, takie jak opóźnienie żądań, wskaźniki błędów i wykorzystanie zasobów. Pomocne mogą być usługi takie jak Sentry, Datadog lub New Relic.
Kwestie dotyczące wdrażania
Trasy API Next.js są przeznaczone do wdrażania na platformach bezserwerowych. Popularne opcje wdrażania obejmują:
- Vercel: Vercel to zalecana platforma do wdrażania aplikacji Next.js. Zapewnia bezproblemową integrację z Next.js i automatycznie optymalizuje aplikację pod kątem wydajności.
- Netlify: Netlify to kolejna popularna platforma bezserwerowa, która obsługuje wdrożenia Next.js. Oferuje funkcje podobne do Vercel, takie jak automatyczne wdrażanie i integracja CDN.
- AWS Lambda: AWS Lambda to usługa obliczeniowa bezserwerowa, która umożliwia uruchamianie kodu bez udostępniania ani zarządzania serwerami. Możesz wdrożyć swoje trasy API Next.js jako funkcje Lambda, używając narzędzi takich jak Serverless Framework lub AWS SAM.
- Google Cloud Functions: Google Cloud Functions to środowisko wykonywania bezserwerowego, które pozwala tworzyć i łączyć usługi w chmurze. Możesz wdrożyć swoje trasy API Next.js jako Cloud Functions, używając narzędzi takich jak Firebase CLI lub Google Cloud SDK.
- Azure Functions: Azure Functions to usługa obliczeniowa bezserwerowa, która umożliwia uruchamianie kodu na żądanie bez zarządzania infrastrukturą. Możesz wdrożyć swoje trasy API Next.js jako Azure Functions, używając narzędzi takich jak Azure Functions Core Tools lub Azure CLI.
Podczas wdrażania aplikacji Next.js z trasami API upewnij się, że zmienne środowiskowe są poprawnie skonfigurowane na platformie wdrażania. Rozważ również czas zimnego startu funkcji bezserwerowych, który może mieć wpływ na początkowy czas odpowiedzi tras API. Optymalizacja kodu i stosowanie technik, takich jak udostępniona współbieżność, mogą pomóc w ograniczeniu problemów z zimnym startem.
Wnioski
Trasy API Next.js zapewniają potężny i wygodny sposób budowania aplikacji full-stack z React. Wykorzystując funkcje bezserwerowe, możesz uprościć tworzenie, zmniejszyć koszty operacyjne i poprawić wydajność aplikacji. Postępując zgodnie z najlepszymi praktykami opisanymi w tym artykule, możesz tworzyć niezawodne i łatwe w utrzymaniu trasy API, które zasilają Twoje aplikacje Next.js.
Niezależnie od tego, czy budujesz prosty formularz kontaktowy, czy złożoną platformę e-commerce, trasy API Next.js mogą pomóc w usprawnieniu procesu tworzenia i zapewnieniu wyjątkowych wrażeń użytkownika.