API Nedir?
API açılımı application programing interface anlamına gelen ve iki farklı teknolojinin haberleşmesini, veri alış veriş yapmasını ya da birbirlerinin yeteneklerini kullanmasını sağlayan bir arayüz sistemidir. İki farklı teknoloji dediğimiz: frontend "react.js, next.js, angular.js vb" ile backend "node.js, php, python, java, .net vb" teknolojilerinin veri alış verişi yapması, mobil uygulamalar "swift, kotlin, flutter, react native vb." ile bulut veri merkezlerinin "firebase, supabase, AWS, mongodb cloud vb." teknolojilerinin veri alış verişi yapması; imdb, hava durumunu saklayan sitelerin paylaştığı bilgileri tek taraflı alma gibi daha bir çok yapıda kullanıyoruz. Bu teknolojilerin ortak olarak kabul ettikleri JSON, TEXT ya da XML dosya türleri API'ların birbirleri ile kolayca iletişim kurmasını sağlıyor.
HTTP Methodları Nelerdir?
API iletişiminin temelini HTTP metodları oluşturur. Her metodun belirli bir amacı vardır ve bu amaca uygun kullanılması hem okunabilirliği hem de güvenliği artırır.
| Method | Amaç | Örnek Kullanım |
|---|---|---|
| GET | Veri okuma | Kullanıcı listesini getir |
| POST | Yeni veri oluşturma | Yeni kullanıcı kaydet |
| PUT | Verinin tamamını güncelleme | Kullanıcı bilgilerini güncelle |
| PATCH | Verinin bir kısmını güncelleme | Sadece email adresini güncelle |
| DELETE | Veri silme | Kullancıyı sil |
Bu metodların doğru kullanılması önemlidir çünkü bir GET isteği ile veri silmeye ya da bir DELETE isteği ile veri oluşturmaya çalışmak hem karışıklığa hem de ciddi güvenlik açıklarına yol açar.
HTTP Durum Kodları Nelerdir?
API iletişiminde her isteğin sonucunda bir durum kodu döner. Bu kodlar isteğin başarılı olup olmadığını ya da neden başarısız olduğunu bize söyler. 3 ana kategoriye ayrılır.
2xx — Başarılı İşlemler
| Kod | Anlamı |
|---|---|
| 200 | İşlem başarılı |
| 201 | Yeni kayıt başarıyla oluşturuldu |
| 204 | İşlem başarılı, döndürülecek içerik yok |
4xx — İşlemci Hataları
| Kod | Anlamı |
|---|---|
| 400 | Eksik ya da hatalı istek (Bad Request) |
| 401 | Kimlik doğrulaması gerekiyor (Unauthorized) |
| 403 | Erişim yetkisi yok (Forbidden) |
| 404 | İstenen kaynak bulunamadı (Not Found) |
| 405 | İzin verilmeyen HHTP methodu (Method Not Allowed) |
5xx — Başarılı İşlemler
| Kod | Anlamı |
|---|---|
| 500 | Sunucu taraflı beklenmedik hata (Internal Server Error) |
| 503 | Sunucu geçici olarak kullanılamıyor (Service Unavailable) |
Bu kodları doğru kullanmak önemlidir. Örneğin bir kimlik doğrulama hatasında 500 dönmek yerine 401 dönmek, hatanın nerede olduğunu çok daha hızlı anlamamızı sağlar.
Next.JS de API Kavramı Nedir?
Next.js yapısı React.js özelliklerini bir tık daha geliştirerek full stack olarak kullanılmasını sağlıyor. Yani frontend'in çalıştığı client tarafını backend'in çalıştığı server tarafı ile beraber çalışmasını sağlıyor. Ancak bu çalışma sadece basit bir fetch ile değil doğrudan server iletişim katmanlarının arasına girip müdahale edilmesi şeklinde oluyor. Yani kullanıcı server'a bir istek attığında istek daha server'a ulaşamadan kontrol edebiliyor ya da server'dan gelen bir mesajı kontrol ederek kullanıcıya döndürebiliyoruz. Bunu sağlamak için Next.JS de yerleşik olan api klasörü altında tanımladığımız route yapılarından ve proxy "Next.js 15 ve öncesinde middleware olarak bilinirdi" katmanından faydalanıyoruz. Bu katmanlar sayesinde iletişimin her iki tarafında da kontrol bizde oluyor.
Route Nedir?
Route kelime anlamı ile yol, rota, hat gibi anlamlara gelen ve bizim backend ile iletişimi sağlayan ara katmanlardan biridir. Bu katman üzerinde CRUD işlemlerinin idaresini sağlayabiliyoruz. CRUD işlemleri sırasında client ya da server tarafında oluşabilecek senaryolara en başından hazırlı olabiliyoruz.
Peki bu yapıyı doğrudan fetch ile kullanmaktan ayıran ne? İkisi de bir URL'e istek atar ama aralarında önemli farklar var.
| Doğrudan Fetch | Route Handler | |
|---|---|---|
| Backend URL'i | Tarayıcıda görünür | Gizli kalır |
| Hata yönetimi | İstemci tarafında yapılır | Sunucu tarafında merkezi yapılır |
| Yetki kontrolü | Her component'te tekrar yazılır | Tek noktada yönetilir |
| Veri filtreleme | Tüm veri istemciye gelir | Sadece gerekli alanlar döndürülür |
| Esneklik | Sınırlı | Token yenileme, loglama vb. eklenebilir |
Bu işlemin fetch API'den en büyük farkı, oluşturduğumuz senaryo sonrası kullanıcıya sadece göstermek istediğimiz verileri ya da hata kodlarını dönerek kullanıcının görmesini istediğimiz şekilde yönlendirebiliyoruz.
Klasör Yapısı Nasıl Olmalı?
Next.js App Router yapısında route'lar dosya sistemi üzerinden tanımlanır. Her klasör bir URL segmentine karşılık gelir ve içinde mutlaka route.ts dosyası bulunması gerekir.
mdsrc/ └── app/ └── api/ ├── auth/ │ ├── login/ │ │ └── route.ts → /api/auth/login │ └── logout/ │ └── route.ts → /api/auth/logout └── user/ ├── get-all/ │ └── route.ts → /api/user/get-all └── [id]/ └── route.ts → /api/user/123
Her route.ts dosyasında hangi HTTP metodunu karşılamak istiyorsak onu export ederiz. Tanımlamadığımız bir metotla istek gelirse Next.js otomatik olarak 405 döner.
ts// src/app/api/user/get-all/route.ts export const GET = async (req: Request) => { ... } // GET isteğini karşılar export const POST = async (req: Request) => { ... } // POST isteğini karşılar
Route Kullanmanın Yararları Nelerdir?
Route katmanında müdahil olabileceğimiz birkaç şeyden bahsedelim.
- Kullanıcı fetch işlemi sırasında doğru bir method ile mi işlemi gerçekleştirmek istediğini kontrol edebilir ve izin vermediğimiz methodlar için "İzin verilmeyen method (405)" hata mesajını geriye döndürebiliriz.
- Kullanıcı fetch body'sine eksik bir bilgi girişi yaptığında "Beklenen değerler eksik ya da hiç gönderilmedi (400)" hata kodunu geri döndürebiliriz. (Bad Request hatası)
- Kullanıcı yetkisi olmayan bir API'a istek attığında "Yetkisiz giriş (403)" hata kodu geri döndürülebilir.
- Kullanıcı başarılı bir istek sonucunda server'dan gelen verinin filtrelenmiş halini görmesini sağlayabiliriz.
Bunlar gibi bir çok hata kodları ya da başarı kodları geri döndürülebilir. Burada amaç iletişim sırasında oluşacak sorunları takip etmek ve hızlıca aksiyon alabilmektir. Bu yapacağımız işlemler ile asıl server sadece doğru bir işlem sirkülasyonunda tetiklenerek bilgi alış verişi sağlanacaktır.
En büyük yararlarından biri de kullanıcı kendi tarayıcısında ağ kısmında doğrudan orijinal backend API linkini görmek yerine sadece localhost/api/user/get-all gibi bir link görecektir. Bu şekilde backend linklerini yetkisiz gözlerden bir nebze saklamış olacaksınız.
Bir kaç senaryo ile bunun kod düzeyinde nasıl çalıştığını örneklendirelim.
GET İşlem Örneği
tsimport { getAccessToken } from "@/libs/auth"; // Temsili kullanıcı token getiren fonksiyon import { NextResponse } from "next/server"; export const GET = async (req: Request) => { try { // Backend API URL'i al const backendApiUrl = process.env.BACKEND_API_URL; // Backend API URL'i kontrol et if (!backendApiUrl) { return NextResponse.json( { error: "Backend API URL is not set" }, { status: 500 }, ); } // API gönderilen linkten query parametlerini alma. const { searchParams } = new URL(req.url); const startDate = searchParams.get("startDate"); const endDate = searchParams.get("endDate"); const accessToken = await getAccessToken(); if (!accessToken) { return NextResponse.json({ error: `Unauthorized` }, { status: 401 }); } let url; // Backend'in kabul ettiği şekilde linki düzenleme if (!startDate || !endDate) { url = `${backendApiUrl}/disasterevents/stats`; } else { const params = new URLSearchParams(); if (startDate) params.set("startDate", startDate); if (endDate) params.set("endDate", endDate); url = `${backendApiUrl}/disasterevents/stats?${params.toString()}`; } // Backend API'ye istek gönder const response = await fetch(url, { method: "GET", headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", }, }); if (!response.ok) { return NextResponse.json( { message: `response: ${response.statusText}` }, { status: 401 }, ); } const disasterData = await response.json(); // Response oluştur const res = NextResponse.json( { message: "Disaster stats fetched successfully", data: disasterData }, { status: 200 }, ); return res; } catch (error: any) { return NextResponse.json( { message: `route error: ${error}`, error }, { status: 500 }, ); } };
POST İşlem Örneği
Bu senaryoda klasik bir login işleminin route katmanında nelerine müdahale ettiğimizi örneklendirelim.
tsimport { serialize } from "cookie"; import { SignJWT } from "jose"; // Bu kütüphane tokene payload yapmak için kullanılır import { NextResponse } from "next/server"; // Json Web Token oluşturma const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET); export const POST = async (req: Request) => { try { // Kullanıcın fetch işleminde body ile gönderdiği değerler alınır const body = await req.json(); // Body den gelen değerlerin boş olmadığı kontrol edilir. if (!body.email || !body.password) { return NextResponse.json( { error: "Email and password are required" }, { status: 400 }, ); } // Backend API URL'i al const backendApiUrl = process.env.BACKEND_USER_API_URL; // Backend API URL'i kontrol et if (!backendApiUrl) { return NextResponse.json( { error: "Backend API URL is not set" }, { status: 500 }, ); } // Backend API'ye istek gönderilir const response = await fetch(`${backendApiUrl}/account/login`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ email: body.email, password: body.password }), }); if (!response.ok) { return NextResponse.json({ message: response }, { status: 401 }); } // Kullanıcı server düzeyinde giriş yapmıştır. const userData = await response.json(); // JWT tokende saklanacak bilgiler const payload = { userId: userData.data.id, userEmail: userData.data.email, userFirstName: userData.data.firstName, userLastName: userData.data.lastName, userRole: userData.data.role, userToken: userData.data.token, }; // JWT token oluştur const token = await new SignJWT(payload) .setProtectedHeader({ alg: "HS256" }) .setExpirationTime("1h") .sign(JWT_SECRET); // Response oluştur const res = NextResponse.json( { message: "Login successful", user: userData.data }, { status: 200 }, ); // Cookie oluştur. Burada amaç kullanıcın bilgilerini her sayfada rahatça ulaşabilmek const cookie = serialize("authToken", token, { httpOnly: true, // Bu değer kullanıcıya sadece server düzeyinde ulaşılabilir olmasını sağlar. Yani client da kimse js düzeyinde bu cookie ulaşamaz. Siz dahil. secure: process.env.NODE_ENV === "production", sameSite: "strict", maxAge: 60 * 60, path: "/", }); res.headers.append("Set-Cookie", cookie); return res; } catch (error: any) { return NextResponse.json({ message: `${error}`, error }, { status: 500 }); } };
Örneklerimiz de GET ve POST işlemlerinin nasıl yapıldığını ve kontrol edebileciğimiz bazı kısımlarını gördük. Bu müdahil olabileceğimiz daha birçok kısmı da mevcut.




