Dowiedz się, jak wykorzystać Trasy API Next.js do budowy backendów bezserwerowych. Przewodnik omawia konfigurację, uwierzytelnianie, utrwalanie danych i inne zaawansowane techniki.
Trasy API Next.js: Zbuduj Swój Backend z Łatwością
Next.js zrewolucjonizował rozwój front-endu dzięki swoim potężnym funkcjom i intuicyjnej strukturze. Ale czy wiesz, że może również znacznie uprościć tworzenie backendu? Trasy API Next.js (API Routes) pozwalają tworzyć bezserwerowe punkty końcowe API bezpośrednio w aplikacji Next.js, w wielu przypadkach eliminując potrzebę posiadania oddzielnego serwera backendowego. Ten kompleksowy przewodnik przeprowadzi Cię przez proces budowania solidnego i skalowalnego backendu przy użyciu Tras API Next.js.
Czym są Trasy API Next.js?
Trasy API to funkcje bezserwerowe, które tworzysz w katalogu /pages/api
w swoim projekcie Next.js. Funkcje te obsługują przychodzące żądania HTTP i zwracają odpowiedzi, podobnie jak tradycyjne API backendowe. Kluczowa różnica polega na tym, że są one wdrażane jako funkcje bezserwerowe, co oznacza, że nie musisz zarządzać serwerami ani infrastrukturą.
Pomyśl o nich jak o lekkich, uruchamianych na żądanie funkcjach backendowych, które są płynnie zintegrowane z Twoim front-endem Next.js.
Korzyści z używania Tras API Next.js
- Uproszczony rozwój: Pisz kod zarówno front-endowy, jak i backendowy w tym samym projekcie, używając JavaScriptu lub TypeScriptu. Koniec z przełączaniem kontekstu między różnymi projektami i technologiami.
- Architektura bezserwerowa: Korzystaj ze skalowalności, niezawodności i opłacalności przetwarzania bezserwerowego. Płać tylko za zasoby, z których korzystasz.
- Łatwe wdrażanie: Wdrażaj całą swoją aplikację (front-end i backend) za pomocą jednego polecenia, używając platform takich jak Vercel czy Netlify.
- Wbudowane bezpieczeństwo: Next.js i platformy bezserwerowe zapewniają wbudowane funkcje bezpieczeństwa do ochrony Twoich punktów końcowych API.
- Poprawiona wydajność: Trasy API mogą być wdrażane bliżej użytkowników, co zmniejsza opóźnienia i poprawia wydajność, co jest szczególnie korzystne dla użytkowników na całym świecie.
- Współużytkowanie kodu: Dziel się kodem między front-endem a backendem, zmniejszając jego duplikację i poprawiając łatwość utrzymania.
Pierwsze kroki z Trasami API Next.js
Stwórzmy prostą trasę API, która zwraca odpowiedź w formacie JSON. Najpierw upewnij się, że masz skonfigurowany projekt Next.js. Jeśli nie, utwórz go za pomocą:
npx create-next-app my-app
cd my-app
Teraz utwórz plik o nazwie hello.js
w katalogu /pages/api
:
// plik: pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ name: 'Jan Kowalski' })
}
Ten kod definiuje prostą trasę API, która odpowiada obiektem JSON zawierającym imię "Jan Kowalski". Aby uzyskać dostęp do tej trasy API, uruchom serwer deweloperski Next.js:
npm run dev
Następnie otwórz przeglądarkę i przejdź do http://localhost:3000/api/hello
. Powinieneś zobaczyć następującą odpowiedź JSON:
{"name": "Jan Kowalski"}
Zrozumienie funkcji obsługującej trasę API (handler)
Funkcja handler
w Twojej trasie API otrzymuje dwa argumenty:
req
: Instancjahttp.IncomingMessage
, która zawiera informacje o przychodzącym żądaniu, takie jak metoda żądania, nagłówki i treść.res
: Instancjahttp.ServerResponse
, która pozwala na wysłanie odpowiedzi z powrotem do klienta.
Możesz używać tych obiektów do obsługi różnych typów żądań, odczytywania danych z treści żądania, ustawiania nagłówków odpowiedzi i wysyłania różnych typów odpowiedzi.
Obsługa różnych metod HTTP
Możesz użyć właściwości req.method
, aby określić metodę HTTP przychodzącego żądania i odpowiednio obsłużyć różne metody. Na przykład:
// plik: pages/api/method.js
export default function handler(req, res) {
if (req.method === 'GET') {
// Obsłuż żądanie GET
res.status(200).json({ message: 'To jest żądanie GET' })
} else if (req.method === 'POST') {
// Obsłuż żądanie POST
res.status(200).json({ message: 'To jest żądanie POST' })
} else {
// Obsłuż inne metody
res.status(405).json({ message: 'Metoda niedozwolona' })
}
}
W tym przykładzie trasa API obsługuje zarówno żądania GET, jak i POST. Jeśli metoda żądania to GET, odpowiada obiektem JSON zawierającym komunikat "To jest żądanie GET". Jeśli metoda żądania to POST, odpowiada obiektem JSON zawierającym komunikat "To jest żądanie POST". Jeśli metoda żądania jest inna, odpowiada błędem 405 Method Not Allowed (Metoda niedozwolona).
Odczytywanie danych z treści żądania
Dla żądań POST, PUT i PATCH często trzeba odczytać dane z treści żądania. Next.js zapewnia wbudowane wsparcie dla parsowania treści żądań w formacie JSON i URL-encoded. Aby sparsować treść żądania JSON, możesz użyć właściwości req.body
. Na przykład:
// plik: pages/api/post.js
export default async function handler(req, res) {
if (req.method === 'POST') {
const { name, email } = req.body
// Przetwórz dane
console.log('Imię:', name)
console.log('Email:', email)
res.status(200).json({ message: 'Dane otrzymane pomyślnie' })
} else {
res.status(405).json({ message: 'Metoda niedozwolona' })
}
}
Aby przetestować tę trasę API, możesz użyć narzędzia takiego jak Postman lub curl do wysłania żądania POST z treścią JSON:
curl -X POST -H "Content-Type: application/json" -d '{"name": "Anna Nowak", "email": "anna.nowak@example.com"}' http://localhost:3000/api/post
Ustawianie nagłówków odpowiedzi
Możesz użyć metody res.setHeader()
, aby ustawić nagłówki odpowiedzi. Jest to przydatne do ustawiania typu zawartości, kontroli pamięci podręcznej (cache control) i innych ważnych informacji. Na przykład:
// plik: pages/api/headers.js
export default function handler(req, res) {
res.setHeader('Content-Type', 'application/json')
res.setHeader('Cache-Control', 's-maxage=3600')
res.status(200).json({ message: 'Witaj, świecie!' })
}
W tym przykładzie trasa API ustawia nagłówek Content-Type
na application/json
, wskazując, że odpowiedź jest obiektem JSON. Ustawia również nagłówek Cache-Control
na s-maxage=3600
, co informuje przeglądarkę i CDN, aby przechowywały odpowiedź w pamięci podręcznej przez maksymalnie 1 godzinę.
Obsługa błędów
Ważne jest, aby elegancko obsługiwać błędy w trasach API. Możesz używać bloków try-catch do przechwytywania wyjątków i wysyłania odpowiednich odpowiedzi o błędach do klienta. Na przykład:
// plik: pages/api/error.js
export default async function handler(req, res) {
try {
// Symuluj błąd
throw new Error('Coś poszło nie tak')
} catch (error) {
console.error(error)
res.status(500).json({ message: 'Wewnętrzny błąd serwera' })
}
}
W tym przykładzie trasa API symuluje błąd, rzucając nowy obiekt Error
. Blok catch przechwytuje błąd, loguje go do konsoli i wysyła do klienta odpowiedź 500 Internal Server Error (Wewnętrzny błąd serwera). Rozważ użycie solidnego systemu logowania, takiego jak Sentry lub Datadog, w środowiskach produkcyjnych.
Łączenie z bazą danych
Jednym z najczęstszych przypadków użycia tras API jest łączenie się z bazą danych. Trasy API Next.js płynnie integrują się z różnymi bazami danych, w tym:
- MongoDB: Popularna baza danych NoSQL, która dobrze nadaje się do elastycznych i nieustrukturyzowanych danych.
- PostgreSQL: Potężna relacyjna baza danych o otwartym kodzie źródłowym, znana ze swojej niezawodności i integralności danych.
- MySQL: Inna popularna relacyjna baza danych o otwartym kodzie źródłowym, szeroko stosowana w aplikacjach internetowych.
- Firebase: Platforma oparta na chmurze, która zapewnia bazę danych w czasie rzeczywistym i inne usługi.
- FaunaDB: Bezserwerowa baza danych zaprojektowana dla aplikacji globalnych.
Oto przykład, jak połączyć się z bazą danych MongoDB w trasie API Next.js:
// plik: pages/api/mongodb.js
import { MongoClient } from 'mongodb'
const uri = process.env.MONGODB_URI
const options = {}
let client
let clientPromise
if (!process.env.MONGODB_URI) {
throw new Error('Dodaj swój URI do Mongo w pliku .env.local')
}
if (process.env.NODE_ENV === 'development') {
// W trybie deweloperskim użyj zmiennej globalnej, aby wartość
// była zachowywana między przeładowaniami modułów spowodowanymi przez HMR (Hot Module Replacement).
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options)
global._mongoClientPromise = client.connect()
}
clientPromise = global._mongoClientPromise
} else {
// W trybie produkcyjnym najlepiej nie używać zmiennej globalnej.
client = new MongoClient(uri, options)
clientPromise = client.connect()
}
// Eksportuj obietnicę MongoClient o zasięgu modułu. Robiąc to w
// osobnym module, klient może być bezpiecznie ponownie używany w wielu
// funkcjach. Zobacz: https://github.com/vercel/next.js/blob/canary/examples/with-mongodb/lib/mongodb.js
export default async function handler(req, res) {
try {
const client = await clientPromise
const db = client.db(process.env.MONGODB_DB)
const collection = db.collection('users')
const users = await collection.find({}).toArray()
res.status(200).json({ users })
} catch (e) {
console.error(e)
res.status(500).json({ message: 'Nie udało się pobrać użytkowników' })
}
}
Przed uruchomieniem tego kodu upewnij się, że masz zainstalowany pakiet mongodb
:
npm install mongodb
Musisz również ustawić zmienne środowiskowe MONGODB_URI
i MONGODB_DB
. Zmienne te powinny być zdefiniowane w pliku .env.local
(lub w ustawieniach zmiennych środowiskowych Twojego dostawcy hostingu dla środowiska produkcyjnego). MONGODB_URI
zawiera ciąg połączenia do Twojej bazy danych MongoDB, a MONGODB_DB
określa nazwę bazy danych.
Uwierzytelnianie i autoryzacja
Ochrona tras API jest kluczowa dla bezpieczeństwa. Trasy API Next.js mogą być zabezpieczone przy użyciu różnych technik uwierzytelniania i autoryzacji, w tym:
- JSON Web Tokens (JWT): Standard bezpiecznego przesyłania informacji między stronami jako obiekt JSON.
- Klucze API: Prosty sposób na ograniczenie dostępu do punktów końcowych API.
- OAuth: Protokół delegacji, który pozwala użytkownikom na udzielanie aplikacjom stron trzecich dostępu do ich zasobów bez udostępniania poświadczeń.
- NextAuth.js: Kompletne, otwarte rozwiązanie do uwierzytelniania dla aplikacji Next.js.
Oto przykład, jak zabezpieczyć trasę API za pomocą uwierzytelniania JWT:
// plik: pages/api/protected.js
import jwt from 'jsonwebtoken'
const secret = process.env.JWT_SECRET
export default function handler(req, res) {
const token = req.headers.authorization?.split(' ')[1]
if (!token) {
return res.status(401).json({ message: 'Brak autoryzacji' })
}
try {
const decoded = jwt.verify(token, secret)
// Obiekt "decoded" zawiera informacje o użytkowniku osadzone w tokenie
// Na przykład: const userId = decoded.userId;
// Kontynuuj przetwarzanie żądania
res.status(200).json({ message: 'Dostęp do chronionego zasobu uzyskany pomyślnie' })
} catch (error) {
return res.status(401).json({ message: 'Nieprawidłowy token' })
}
}
Przed uruchomieniem tego kodu upewnij się, że masz zainstalowany pakiet jsonwebtoken
:
npm install jsonwebtoken
Musisz również ustawić zmienną środowiskową JWT_SECRET
. Powinien to być silny, losowo wygenerowany klucz tajny, używany do podpisywania i weryfikacji tokenów JWT. Przechowuj go bezpiecznie i nigdy nie ujawniaj w kodzie po stronie klienta.
Middleware
Chociaż Next.js nie oferuje tradycyjnego middleware dla tras API w taki sam sposób jak Express.js, można osiągnąć podobną funkcjonalność, opakowując funkcje obsługujące trasy API w funkcje wielokrotnego użytku. Pozwala to na wykonywanie zadań takich jak:
- Uwierzytelnianie: Weryfikacja poświadczeń użytkownika przed udzieleniem dostępu do punktów końcowych API.
- Autoryzacja: Sprawdzanie, czy użytkownik ma niezbędne uprawnienia do wykonania określonej akcji.
- Logowanie: Rejestrowanie przychodzących żądań i wychodzących odpowiedzi w celach audytowych i debugowania.
- Walidacja: Sprawdzanie poprawności danych żądania, aby upewnić się, że spełniają określone kryteria.
- Ograniczanie liczby żądań (Rate Limiting): Ochrona API przed nadużyciami poprzez ograniczenie liczby żądań, które użytkownik może wykonać w danym okresie czasu.
Oto przykład, jak stworzyć proste middleware do logowania:
// plik: utils/middleware.js
export function withLogging(handler) {
return async function(req, res) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`)
return handler(req, res)
}
}
Aby użyć tego middleware, po prostu opakuj swoją funkcję obsługującą trasę API funkcją withLogging
:
// plik: pages/api/logged.js
import { withLogging } from '../../utils/middleware'
async function handler(req, res) {
res.status(200).json({ message: 'To żądanie zostało zarejestrowane' })
}
export default withLogging(handler)
Dobre praktyki budowania tras API w Next.js
- Utrzymuj swoje trasy API małe i skoncentrowane. Każda trasa API powinna obsługiwać określone zadanie lub zasób.
- Używaj zmiennych środowiskowych do przechowywania wrażliwych danych. Nigdy nie umieszczaj na stałe w kodzie tajemnic ani kluczy API.
- Waliduj dane żądania, aby zapobiegać lukom w zabezpieczeniach. Użyj biblioteki takiej jak Joi lub Yup do walidacji treści żądań.
- Obsługuj błędy w sposób elegancki i dostarczaj informacyjne komunikaty o błędach. Używaj bloków try-catch i loguj błędy w centralnej lokalizacji.
- Używaj buforowania (caching) w celu poprawy wydajności. Buforuj często używane dane, aby zmniejszyć obciążenie bazy danych.
- Monitoruj swoje trasy API pod kątem wydajności i błędów. Użyj narzędzia do monitorowania, takiego jak Sentry lub Datadog, aby śledzić stan swojego API.
- Dokumentuj swoje trasy API za pomocą narzędzia takiego jak Swagger lub OpenAPI. Ułatwia to innym programistom korzystanie z Twojego API.
- Rozważ użycie TypeScriptu dla bezpieczeństwa typów. TypeScript może pomóc Ci wczesniej wykrywać błędy i poprawić łatwość utrzymania kodu.
- Myśl o internacjonalizacji (i18n) od samego początku. Jeśli Twoja aplikacja będzie używana przez użytkowników z różnych krajów, zaprojektuj swoje trasy API tak, aby obsługiwały wiele języków i walut. Na przykład, punkty końcowe API dla e-commerce mogą potrzebować obsługi różnych stawek podatkowych i kosztów wysyłki w zależności od lokalizacji użytkownika.
- Zaimplementuj odpowiednią konfigurację CORS (Cross-Origin Resource Sharing). Jest to kluczowe, gdy dostęp do Twojego API odbywa się z innej domeny niż Twoja aplikacja Next.js. Starannie skonfiguruj CORS, aby zezwolić na dostęp do zasobów API tylko autoryzowanym źródłom.
Zaawansowane techniki
Zadania w tle (Background Jobs)
W przypadku długotrwałych zadań, które nie powinny blokować odpowiedzi API, rozważ użycie zadań w tle. Możesz użyć bibliotek takich jak BullMQ lub Bree do zarządzania zadaniami w tle i przetwarzania ich asynchronicznie.
WebSockets
Dla aplikacji czasu rzeczywistego możesz używać WebSockets w swoich trasach API Next.js. Biblioteki takie jak Socket.IO i ws ułatwiają ustanawianie trwałych połączeń między klientem a serwerem.
GraphQL
Jeśli potrzebujesz bardziej elastycznego i wydajnego sposobu na pobieranie danych, rozważ użycie GraphQL. Możesz użyć bibliotek takich jak Apollo Server lub Yoga, aby utworzyć punkt końcowy API GraphQL w swojej aplikacji Next.js.
Podsumowanie
Trasy API Next.js zapewniają potężny i wygodny sposób na budowanie bezserwerowych backendów bezpośrednio w aplikacji Next.js. Wykorzystując zalety architektury bezserwerowej, możesz uprościć rozwój, poprawić wydajność i obniżyć koszty. Niezależnie od tego, czy budujesz prosty formularz kontaktowy, czy złożoną platformę e-commerce, trasy API Next.js mogą pomóc Ci z łatwością stworzyć solidny i skalowalny backend. Dzięki solidnemu zrozumieniu podstaw i stosowaniu dobrych praktyk możesz wykorzystać to potężne narzędzie do tworzenia wydajnych, bezpiecznych i globalnie dostępnych aplikacji.