Bir Express API, Node.js ile arka uç geliştirmenin en hızlı yollarından biridir: birkaç satırla bir HTTP sunucusu ayağa kaldırır, isteklere yanıt verir ve büyüdükçe katmanlara ayrılabilir. Bu yazıda küçük ama gerçek bir REST API'yi sıfırdan kuracağız; router'larla rotaları düzenleyecek, middleware ile ortak işleri tek yerde toplayacak ve sonunda dağınık try/catch bloklarından kurtaran merkezi bir hata yönetimi kuracağız.
Projeyi başlatmak
Önce boş bir klasör açıp Node projesini ve Express'i kuruyoruz. Node.js 18 veya üzeri önerilir; bu sürümler fetch ve modern JavaScript özelliklerini yerel olarak destekler.
mkdir blog-api && cd blog-api
npm init -y
npm install express
npm install --save-dev nodemon
package.json içine ES modüllerini açmak için "type": "module" ekleyin; böylece import sözdizimini kullanabilirsiniz. Geliştirme sırasında dosya değişince sunucuyu yeniden başlatması için bir script de tanımlayalım:
{
"type": "module",
"scripts": {
"dev": "nodemon server.js",
"start": "node server.js"
}
}
İlk sunucu
Express uygulamasının çekirdeği bir app nesnesidir. Gelen JSON gövdelerini ayrıştırmak için express.json() middleware'ini ekleyip basit bir sağlık kontrolü rotasıyla başlıyoruz.
// server.js
import express from "express";
const app = express();
app.use(express.json());
app.get("/health", (req, res) => {
res.json({ status: "ok", uptime: process.uptime() });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`API çalışıyor: http://localhost:${PORT}`);
});
npm run dev deyip http://localhost:3000/health adresine gittiğinizde JSON yanıtı görmelisiniz. İşte bu kadar; bir API'niz var. Şimdi onu büyütmeye hazır hale getirelim.
Router ile rotaları ayırmak
Her rotayı server.js içine yığmak küçük projelerde işe yarar ama hızla okunamaz hale gelir. Express'in Router nesnesi, ilgili rotaları ayrı bir dosyada gruplamanı ve uygulamaya tek bir önekle bağlamanı sağlar. Diyelim ki yazılar (posts) için bir kaynak yönetiyoruz.
// routes/posts.js
import { Router } from "express";
const router = Router();
const posts = [
{ id: 1, title: "Merhaba dünya", body: "İlk yazı" }
];
router.get("/", (req, res) => {
res.json(posts);
});
router.get("/:id", (req, res) => {
const post = posts.find(p => p.id === Number(req.params.id));
if (!post) {
return res.status(404).json({ error: "Yazı bulunamadı" });
}
res.json(post);
});
router.post("/", (req, res) => {
const { title, body } = req.body;
const post = { id: posts.length + 1, title, body };
posts.push(post);
res.status(201).json(post);
});
export default router;
Ardından bu router'ı ana uygulamaya /posts öneki altında bağlıyoruz:
import postsRouter from "./routes/posts.js";
app.use("/posts", postsRouter);
Artık GET /posts, GET /posts/1 ve POST /posts çalışıyor. Her yeni kaynak (kullanıcılar, yorumlar) kendi router dosyasına gider; server.js sade kalır.
Middleware: ortak işleri tek yerde toplamak
Middleware, isteğin yanıta ulaşmadan önce geçtiği bir fonksiyon zinciridir. İmzası (req, res, next) şeklindedir; işini bitirince next() çağırarak sırayı bir sonrakine devreder. Loglama, kimlik doğrulama, hız sınırlama gibi kesişen ilgileri burada toplarsın. Basit bir istek logu yazalım:
// middleware/logger.js
export function logger(req, res, next) {
const start = Date.now();
res.on("finish", () => {
const ms = Date.now() - start;
console.log(`${req.method} ${req.originalUrl} ${res.statusCode} - ${ms}ms`);
});
next();
}
Bunu tüm rotalardan önce ekleyince her istek otomatik loglanır:
import { logger } from "./middleware/logger.js";
app.use(logger);
Doğrulama da bir middleware olabilir. Örneğin POST /posts öncesinde gövdeyi kontrol eden küçük bir koruyucu:
export function validatePost(req, res, next) {
const { title } = req.body;
if (!title || title.trim() === "") {
return res.status(400).json({ error: "title zorunludur" });
}
next();
}
Sonra yalnızca o rotaya iliştir: router.post("/", validatePost, handler). Middleware'lerin gücü buradadır: tekrar eden mantığı bir kez yazıp her yere takabilirsin.
Merkezi hata yönetimi
Express'in en çok işe yarayan özelliklerinden biri, dört parametreli özel bir hata middleware'idir: (err, req, res, next). Bu fonksiyon tüm rotalardan sonra tanımlanır ve herhangi bir yerde fırlatılan hatayı yakalar. Böylece her handler içine ayrı ayrı try/catch serpiştirmek zorunda kalmazsın.
Async fonksiyonlardaki hataların buraya ulaşması için küçük bir sarmalayıcı kullanmak en temiz yoldur (Express 5 async hataları otomatik iletir, ama 4.x'te bu yardımcı pratiktir):
// utils/asyncHandler.js
export const asyncHandler = (fn) => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
Bir hata sınıfı tanımlayıp asyncHandler içinde rahatça fırlatabiliriz:
// utils/ApiError.js
export class ApiError extends Error {
constructor(status, message) {
super(message);
this.status = status;
}
}
Son olarak hata middleware'ini ve bilinmeyen rotalar için bir 404 yakalayıcısını ekliyoruz. Bunlar her zaman en sonda durmalı:
// 404 — eşleşen rota yoksa
app.use((req, res) => {
res.status(404).json({ error: "Kaynak bulunamadı" });
});
// Merkezi hata yönetimi
app.use((err, req, res, next) => {
const status = err.status || 500;
if (status === 500) console.error(err);
res.status(status).json({ error: err.message || "Sunucu hatası" });
});
Artık bir handler içinde throw new ApiError(404, "Yazı bulunamadı") demek yeterli; geri kalanı bu tek nokta hallediyor. Üretim ortamında 500 hatalarının ayrıntısını istemciye dökmemek için yukarıdaki gibi mesajı sadeleştirmek iyi bir alışkanlıktır.
Yapıyı temiz tutmak
Proje büyüdükçe basit bir klasör düzeni işleri sürdürülebilir kılar:
routes/— her kaynak için bir router dosyası.controllers/— rotayı işleyen asıl iş mantığı (router'ı ince tutar).middleware/— logger, auth, doğrulama gibi yeniden kullanılabilir parçalar.utils/—asyncHandler,ApiErrorgibi yardımcılar.
Bu ayrım sayesinde router yalnızca "hangi URL hangi fonksiyona gider" sorusunu yanıtlar; gerçek iş controller'da olur. Veritabanı eklediğinde (örneğin PostgreSQL ya da MongoDB) bu yapı neredeyse hiç değişmeden ölçeklenir.
Sık Sorulan Sorular
Express yerine yerleşik http modülünü kullansam olmaz mı?
Olur, ama routing, gövde ayrıştırma ve middleware zincirini elle yazman gerekir. Express bunların hepsini hafif bir katmanla sağlar; öğrenmesi kolay ve ekosistemi çok geniştir. Küçük bir API için bile zamandan kazandırır.
Middleware sırası neden önemli?
Express middleware'leri app.use ile eklendikleri sıraya göre çalıştırır. Örneğin express.json() gövdeyi ayrıştırmadan önce doğrulama yapamazsın; hata middleware'i de tüm rotalardan sonra gelmeli ki onların hatalarını yakalayabilsin.
Express 4 mü 5 mi kullanmalıyım?
Express 5 artık kararlı ve async handler'lardaki hataları otomatik olarak hata middleware'ine iletir. Yeni projelerde 5'i tercih edebilirsin; mevcut 4.x projelerinde yukarıdaki asyncHandler deseni güvenli ve yaygın bir çözümdür.
API'nizi bir üst seviyeye taşımak mı istiyorsunuz? Kimlik doğrulama, veritabanı entegrasyonu veya üretime hazır bir Express altyapısı kurmanız gerekiyorsa, birlikte bakabiliriz. Benimle iletişime geçin.