Takip et

Mastering Routing in Express ile TypeScript

Karmaşık API’lerinizin yönlendirme mantığını yönetmekte zorlanıyor musunuz? Özellikle büyük ve ölçeklenebilir uygulamalar geliştirirken, Express.js’in esnek yapısı TypeScript’in tip güvenliği ile birleştiğinde, yönlendirme (routing) süreçlerinizde devrim yaratabilir. Bu makalede, Express.js ve TypeScript kullanarak sağlam, bakımı kolay ve performanslı bir yönlendirme altyapısı nasıl inşa edeceğinizi adım adım keşfedeceğiz. Gelişmiş middleware tekniklerinden modüler mimarilere, tip güvenli rotalardan performans ipuçlarına kadar her şeyi derinlemesine inceleyeceğiz. Hazır olun, çünkü Express ve TypeScript ile API geliştirme yolculuğunuzda yeni bir seviyeye ulaşacaksınız.

Web uygulamalarının belkemiğini oluşturan API’ler, kullanıcıların veya diğer sistemlerin sunucunuzla etkileşim kurmasını sağlar. Bu etkileşimin ilk ve en kritik adımı ise yönlendirmedir. Yani, gelen bir isteğin (HTTP Request) hangi kaynağa yönlendirileceğini ve hangi kod bloğu tarafından işleneceğini belirleme sürecidir. Express.js, Node.js ekosisteminin en popüler ve minimalist web çatılarından biridir ve bu yönlendirme işlemini son derece basit ve esnek bir şekilde gerçekleştirmenize olanak tanır. Ancak, projeler büyüdükçe bu esneklik, iyi yönetilmediğinde bir karmaşaya dönüşebilir.

Peki, bu noktada TypeScript neden devreye giriyor? JavaScript’in dinamik doğası, küçük projelerde hızlı prototipleme için harika olsa da, büyük ölçekli ve ekip tabanlı projelerde hata ayıklama (debugging) ve bakım maliyetlerini artırabilir. TypeScript, JavaScript’e statik tipleme yeteneği ekleyerek bu sorunları çözer. Geliştiricilerin kod yazarken hataları erken yakalamasına, daha okunabilir ve öngörülebilir kodlar üretmesine yardımcı olur. Express.js ile TypeScript’i birleştirdiğimizde, hem Express’in hafif ve güçlü yönlendirme yeteneklerinden faydalanırız hem de TypeScript’in sunduğu tip güvenliği sayesinde daha robust ve ölçeklenebilir API’ler inşa ederiz. Bu sinerji, özellikle mikroservis mimarilerinde veya büyük monolitik uygulamalarda kritik öneme sahiptir.

Yönlendirme, sadece URL’leri fonksiyonlarla eşleştirmekten çok daha fazlasıdır. İyi bir yönlendirme mimarisi, uygulamanızın bakımını kolaylaştırır, yeni özellikler eklemeyi hızlandırır ve hataların yayılmasını engeller. Örneğin, bir e-ticaret uygulamasında “/products” rotası tüm ürünleri listelerken, “/products/:id” rotası belirli bir ürünü getirir. Bu rotaların her birinin kendine özgü iş mantığı ve veri erişim gereksinimleri vardır. Bu iş mantığını düzgün bir şekilde ayırmak ve yönetmek, uygulamanızın performansını ve güvenliğini doğrudan etkiler. Aynı zamanda, ekip içinde farklı geliştiricilerin aynı anda farklı rotalar üzerinde çalışabilmesini sağlar, bu da geliştirme süreçlerini hızlandırır. Temel Express yönlendirmesi, app.get(), app.post() gibi yöntemlerle basitçe HTTP metotlarını belirli bir yola atamanızı sağlar. Ancak gerçek dünya senaryolarında bu yaklaşım hızla yetersiz kalır ve daha yapılandırılmış bir çözüme ihtiyaç duyulur. Bu nedenle, yönlendirme stratejileri üzerinde ustalaşmak, modern web geliştirme dünyasında vazgeçilmez bir beceridir.


// app.ts - Temel Express ve TypeScript kurulumu
import express, { Application, Request, Response } from 'express';

const app: Application = express();
const PORT: number = 3000;

// Body parser middleware'i
app.use(express.json());

// Temel GET rotası
app.get('/', (req: Request, res: Response) => {
  res.send('Merhaba Dünya, Express ve TypeScript ile!');
});

// Dinamik parametreli GET rotası
app.get('/kullanicilar/:id', (req: Request, res: Response) => {
  const userId: string = req.params.id;
  res.send(Kullanıcı ID: ${userId} bilgileri getiriliyor.);
});

// POST rotası örneği
app.post('/urunler', (req: Request, res: Response) => {
  const { ad, fiyat } = req.body;
  if (!ad || !fiyat) {
    return res.status(400).send('Ürün adı ve fiyatı zorunludur.');
  }
  res.status(201).send(Ürün oluşturuldu: ${ad} - ${fiyat} TL);
});

app.listen(PORT, () => {
  console.log(Sunucu http://localhost:${PORT} adresinde çalışıyor.);
});

Middleware ile Güçlü Yönlendirme Katmanları Nasıl Oluşturulur?

Express.js'in en güçlü özelliklerinden biri olan middleware, HTTP istek-cevap döngüsünün her aşamasında özel fonksiyonlar çalıştırmanıza olanak tanır. Adından da anlaşılacağı gibi, "orta katman yazılım" (middleware) istek gelip cevaba dönüşmeden önce veya sonra çalışan bir dizi fonksiyondur. Bu fonksiyonlar, isteği analiz edebilir, değiştirebilir, sonlandırabilir veya bir sonraki middleware'e ya da rota işleyicisine devredebilir. TypeScript ile middleware kullanmak, bu fonksiyonların beklenen parametre tiplerini ve dönüş tiplerini açıkça tanımlayarak kodunuzu daha güvenli ve hata yakalamayı kolay hale getirir. Middleware'ler, bir uygulamanın birçok farklı yönlendirme işlevini yerine getirmek için vazgeçilmezdir, örneğin kimlik doğrulama, yetkilendirme, veri doğrulama, loglama ve hata yönetimi gibi.

Middleware'ler uygulamanın geneline (app.use()), belirli bir router grubuna (router.use()) veya tek bir rotaya özgü olarak (app.get('/', myMiddleware, handler)) uygulanabilir. Örneğin, bir API anahtarı doğrulama middleware'i, her gelen isteğin geçerli bir API anahtarı içerip içermediğini kontrol edebilir. Eğer anahtar eksik veya geçersizse, isteği hemen sonlandırabilir ve bir hata mesajı döndürebilir. Bu sayede, her rota işleyicisine aynı doğrulama mantığını tekrar tekrar yazmak zorunda kalmazsınız, bu da kod tekrarını önler ve bakım kolaylığı sağlar. Özellikle büyük ölçekli uygulamalarda, middleware zincirleri oluşturarak karmaşık iş akışlarını yönetmek çok daha pratik hale gelir. Örneğin, bir istek önce loglama middleware'inden, sonra kimlik doğrulama middleware'inden, ardından yetkilendirme middleware'inden geçebilir ve son olarak asıl rota işleyicisine ulaşabilir.

Hata yönetimi de middleware'lerin kritik bir kullanım alanıdır. Express'te özel bir hata middleware'i tanımlayarak (dört parametre alan bir fonksiyon: (err, req, res, next)), uygulamanızda meydana gelen beklenmedik hataları merkezi bir yerde yakalayabilir ve kullanıcıya tutarlı hata mesajları döndürebilirsiniz. Bu, hem kullanıcı deneyimini iyileştirir hem de uygulamanızın güvenliğini artırır, çünkü hassas hata bilgilerinin dışarı sızmasını engellersiniz. TypeScript ile bu hata middleware'ini tip açısından güvenli hale getirmek, farklı hata tiplerini daha etkili bir şekilde işlemenize olanak tanır. Bir başka gerçek dünya senaryosu ise, kullanıcının belirli bir kaynağa erişim yetkisinin olup olmadığını kontrol eden bir yetkilendirme middleware'i olabilir. Diyelim ki, sadece "admin" rolüne sahip kullanıcılar belirli bir API uç noktasına erişebilsin. Bu kontrolü her rotada yapmak yerine, tek bir yetkilendirme middleware'i yazarak kodu çok daha temiz tutabiliriz.

Uzman İpucu: Middleware'leri mantıksal gruplara ayırmak için ayrı dosyalarda tanımlayın (örneğin, src/middleware/auth.ts). Bu, kod okunabilirliğini artırır ve projenizin organizasyonunu güçlendirir. Özellikle çok sayıda middleware kullanıyorsanız bu yaklaşım kurtarıcı olacaktır.

// src/middleware/auth.ts
import { Request, Response, NextFunction } from 'express';

// Request tipine özel bir alan eklemek için genişletme
// Bu, request objesine userId gibi özel veriler eklemenize olanak tanır.
declare global {
  namespace Express {
    interface Request {
      userId?: string;
      userRole?: 'admin' | 'editor' | 'guest';
    }
  }
}

export const authenticateAPIKey = (req: Request, res: Response, next: NextFunction) => {
  const apiKey = req.headers['x-api-key'] as string;

  if (!apiKey || apiKey !== 'SECRET_API_KEY') {
    return res.status(401).send('Yetkisiz Erişim: Geçersiz veya Eksik API Anahtarı');
  }

  // Örnek olarak kullanıcı ID'si ekleyelim
  req.userId = 'sample_user_123';
  req.userRole = 'admin'; // Rol ataması
  next(); // Bir sonraki middleware'e veya rota işleyicisine geç
};

export const authorizeRole = (requiredRole: 'admin' | 'editor' | 'guest') => {
  return (req: Request, res: Response, next: NextFunction) => {
    if (!req.userRole || req.userRole !== requiredRole) {
      return res.status(403).send('Yasak: Bu kaynağa erişim yetkiniz yok.');
    }
    next();
  };
};

// src/app.ts (veya router dosyası) içinde kullanım
import express, { Application, Request, Response, NextFunction } from 'express';
import { authenticateAPIKey, authorizeRole } from './middleware/auth';

const app: Application = express();
app.use(express.json());

// Tüm rotalar için API anahtarı doğrulamasını uygula
app.use(authenticateAPIKey);

app.get('/public', (req: Request, res: Response) => {
  res.send('Bu herkese açık bir rota.');
});

// Sadece adminlerin erişebileceği bir rota
app.get('/admin-dashboard', authorizeRole('admin'), (req: Request, res: Response) => {
  res.send(Yönetici Paneli. Giriş yapan kullanıcı ID: ${req.userId}, Rol: ${req.userRole});
});

// Hata yönetimi middleware'i (her zaman diğer middleware'lerden sonra gelmeli)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);
  res.status(500).send('Sunucu tarafında bir hata oluştu.');
});

Modüler ve Ölçeklenebilir Routing Mimarisi Nasıl Tasarlanır?

Büyük ve karmaşık uygulamalar geliştirirken, tüm rotaları tek bir dosyada tutmak hızla yönetilemez bir hale gelir. Kod tekrarı, düşük okunabilirlik ve zorlu bakım süreçleri kaçınılmaz olur. İşte bu noktada modüler yönlendirme mimarileri devreye girer. Express.js'in express.Router() sınıfı, bu modülerliği sağlamak için tasarlanmıştır. express.Router() ile belirli bir kaynak veya iş alanı (örneğin, kullanıcılar, ürünler, siparişler) için kendi özel yönlendirme örneklerini oluşturabilir, bunları ayrı dosyalarda tanımlayabilir ve daha sonra ana Express uygulamasına bağlayabilirsiniz. Bu yaklaşım, uygulamanızın farklı bölümlerinin bağımsız olarak geliştirilmesini ve bakımını kolaylaştırır.

Modüler bir yapı kurarken, genellikle kaynak bazlı bir organizasyon tercih edilir. Yani, her bir kaynağın (users, products, orders) kendi router dosyası, controller'ı ve muhtemelen servis katmanı olur. Örneğin, src/routes/userRoutes.ts dosyası tüm kullanıcılarla ilgili rotaları tanımlarken, src/controllers/userController.ts bu rotaların iş mantığını (kullanıcı oluşturma, güncelleme, silme) barındırır. Bu ayrım, kodun sorumluluklarını net bir şekilde belirler (Separation of Concerns prensibi) ve projenizin genel mimarisini daha düzenli hale getirir. Her dosya veya modül sadece belirli bir göreve odaklandığı için, bir hatanın kaynağını bulmak veya yeni bir özellik eklemek çok daha kolaylaşır. Bu yaklaşım, uygulamanızı test edilebilirliği açısından da büyük avantajlar sunar, çünkü her bir modül bağımsız olarak test edilebilir.

Gerçek dünya senaryosunda, büyük bir e-ticaret platformunu düşünelim. Bu platformda ürünler, kullanıcılar, siparişler, ödemeler gibi birçok farklı modül bulunur. Her modülün kendine ait API uç noktaları vardır. Modüler yönlendirme sayesinde, /api/v1/products ile ilgili tüm rotalar productRoutes.ts içinde, /api/v1/users ile ilgili rotalar ise userRoutes.ts içinde tanımlanabilir. Ana app.ts dosyası sadece bu router'ları uygulamanın belirli bir yol öneki altında bağlar. Örneğin, app.use('/api/v1/products', productRouter) şeklinde bir kullanım, ürün API'lerine giden tüm isteklerin productRouter tarafından işlenmesini sağlar. Ayrıca, API versiyonlama (/api/v1, /api/v2) için de bu modüler yapı idealdir. Eski versiyon rotalarını korurken yeni versiyonlar için ayrı router'lar tanımlayabilirsiniz. Bu esneklik, API'nizin evrim geçirmesini ve geriye dönük uyumluluğu sürdürmesini kolaylaştırır, bu da uzun ömürlü ve sürdürülebilir bir yazılım geliştirme için kritik bir faktördür.


// src/controllers/userController.ts
import { Request, Response } from 'express';

// Tip güvenliği için Request parametrelerini tanımlayalım
interface UserParams {
  id: string;
}

interface UserBody {
  ad: string;
  email: string;
}

export const getAllUsers = (req: Request, res: Response) => {
  // Veritabanından tüm kullanıcıları çekme mantığı
  const users = [{ id: '1', ad: 'Ali', email: 'ali@example.com' }];
  res.json(users);
};

export const getUserById = (req: Request, res: Response) => {
  const { id } = req.params;
  // Veritabanından belirli bir kullanıcıyı çekme mantığı
  const user = { id, ad: Kullanıcı ${id}, email: user${id}@example.com };
  res.json(user);
};

export const createUser = (req: Request<{}, {}, UserBody>, res: Response) => {
  const { ad, email } = req.body;
  if (!ad || !email) {
    return res.status(400).send('Ad ve email zorunludur.');
  }
  // Yeni kullanıcıyı veritabanına kaydetme mantığı
  const newUser = { id: String(Date.now()), ad, email };
  res.status(201).json(newUser);
};

// src/routes/userRoutes.ts
import { Router } from 'express';
import { getAllUsers, getUserById, createUser } from '../controllers/userController';
import { authenticateAPIKey, authorizeRole } from '../middleware/auth';

const router = Router();

// Middleware'leri router seviyesinde uygulayabiliriz
// router.use(authenticateAPIKey); // Tüm kullanıcı rotaları için kimlik doğrulama

router.get('/', getAllUsers);
router.get('/:id', getUserById);
router.post('/', authorizeRole('admin'), createUser); // Sadece adminler yeni kullanıcı oluşturabilir

export default router;

// src/app.ts
import express, { Application } from 'express';
import userRoutes from './routes/userRoutes';
import productRoutes from './routes/productRoutes'; // Örnek olarak bir ürün rotası da ekleyelim
import { authenticateAPIKey } from './middleware/auth'; // Ana middleware'i de buraya ekleyebiliriz

const app: Application = express();
const PORT: number = 3000;

app.use(express.json());

// Uygulama genelinde geçerli middleware'ler
app.use(authenticateAPIKey);

// Modüler rotaları bağla
app.use('/api/v1/users', userRoutes);
// app.use('/api/v1/products', productRoutes); // Ürün rotalarını da benzer şekilde bağlayabiliriz

app.listen(PORT, () => {
  console.log(Sunucu http://localhost:${PORT} adresinde çalışıyor.);
});

TypeScript ile Tip Güvenli Rotalar ve Parametreler Nasıl Yönetilir?

TypeScript'in en büyük faydası, tip güvenliği sağlamasıdır. Express.js ile çalışırken bu, özellikle HTTP isteklerinden gelen verileri (parametreler, sorgu dizeleri, istek gövdesi) işlerken hayati önem taşır. Varsayılan olarak, Express'teki req.params, req.query ve req.body gibi alanlar any tipindedir, bu da geliştirme sırasında potansiyel hataları gizleyebilir. TypeScript'i doğru kullanarak, bu alanlara beklenen tipleri atayabilir ve böylece çalışma zamanı hatalarını derleme zamanında yakalayabilirsiniz. Bu, daha öngörülebilir, bakımı kolay ve güvenilir kod yazmanızı sağlar.

Tip güvenliği sağlamanın ilk adımı, isteğin farklı bölümleri için özel arayüzler (interfaces) veya tipler (types) tanımlamaktır. Örneğin, bir kullanıcının ID'sini içeren bir URL parametresi için UserParams arayüzü, bir kullanıcı oluşturma isteğinin gövdesi için UserBody arayüzü tanımlayabilirsiniz. Express'in Request tipi, generik tipleri kabul eder ve bu sayede Request şeklinde tip tanımlamaları yapabilirsiniz. Bu, rota işleyicinizin veya middleware'inizin beklediği veri yapısını açıkça belirtir. Bu sayede, yanlış bir alana erişmeye çalıştığınızda veya yanlış bir tipte veri göndermeye kalktığınızda TypeScript derleyicisi sizi uyaracaktır. Bu, özellikle büyük ekiplerde ve karmaşık API'lerde kod kalitesini önemli ölçüde artırır ve belgeleme ihtiyacını azaltır, çünkü tipler kodun kendisi için bir belge görevi görür.

Veri doğrulama (validation) da tip güvenli rotaların ayrılmaz bir parçasıdır. TypeScript tek başına veri tiplerini derleme zamanında kontrol etse de, çalışma zamanında gelen verilerin (örneğin bir POST isteğinin gövdesi) belirli kurallara (e-posta formatı, sayı aralığı vb.) uygun olup olmadığını garanti etmez. Bu noktada Zod veya Joi gibi güçlü validasyon kütüphaneleri devreye girer. Bu kütüphaneleri TypeScript ile entegre ederek, hem veri yapılarını tip açısından tanımlayabilir hem de çalışma zamanında doğrulayabilirsiniz. Örneğin, Zod ile bir şema tanımladığınızda, bu şemadan otomatik olarak bir TypeScript tipi türetebilirsiniz. Gelen isteğin gövdesini bu şemaya göre doğrulayıp, doğrulanan veriyi güvenli bir şekilde kullanabilirsiniz. Böylece, API'nize gelen verinin her zaman beklediğiniz formatta olacağından emin olursunuz, bu da uygulamanızın kırılganlığını azaltır ve hata oranını düşürür. Ayrıca, bu yaklaşım, uygulamanızın güvenliğini de artırır, çünkü beklenmeyen veya kötü amaçlı verilerin sisteminize sızmasını engeller.

Uzman İpucu: Express'in Request objesini genişletmek için declare global mekanizmasını kullanın. Bu sayede, middleware'leriniz tarafından req objesine eklenen özel alanları (req.userId gibi) tip güvenli hale getirebilirsiniz. Bu, özellikle kimlik doğrulama veya yetkilendirme sonrası kullanıcı bilgilerini taşıyan senaryolarda çok kullanışlıdır.

// src/types/express.d.ts - Express Request tipini genişletmek için
// Bu dosyanın tsconfig.json tarafından include edildiğinden emin olun
import { Request } from 'express';

declare global {
  namespace Express {
    interface Request {
      // Middleware'ler tarafından eklenecek özel alanlar
      user?: {
        id: string;
        email: string;
        role: 'admin' | 'user' | 'guest';
      };
      // Diğer özel tipler buraya eklenebilir
    }
  }
}

// src/schemas/userSchema.ts (örneğin, Zod ile)
import { z } from 'zod';

// Kullanıcı oluşturma isteği için şema
export const createUserSchema = z.object({
  ad: z.string().min(3, 'Ad en az 3 karakter olmalıdır.'),
  email: z.string().email('Geçerli bir e-posta adresi giriniz.'),
  sifre: z.string().min(6, 'Şifre en az 6 karakter olmalıdır.'),
});

// Zod şemasından TypeScript tipi türetme
export type CreateUserBody = z.infer;

// src/controllers/userController.ts (güncellenmiş)
import { Request, Response } from 'express';
import { CreateUserBody } from '../schemas/userSchema'; // Tipimizi import ettik

interface UserParams {
  id: string;
}

// req.body için CreateUserBody tipini kullandık
export const createUser = (req: Request<{}, {}, CreateUserBody>, res: Response) => {
  const { ad, email, sifre } = req.body; // Artık ad, email, sifre tip güvenli!
  
  // Burada Zod ile çalışma zamanı validasyonu da yapabiliriz
  try {
    createUserSchema.parse(req.body); // Gelen veriyi şemaya göre doğrula
  } catch (error) {
    return res.status(400).json(error); // Validasyon hatasını döndür
  }

  // Kullanıcıyı veritabanına kaydetme mantığı
  const newUser = { id: String(Date.now()), ad, email, sifre, role: 'user' };
  res.status(201).json(newUser);
};

// src/routes/userRoutes.ts (güncellenmiş)
import { Router } from 'express';
import { createUser } from '../controllers/userController';
import { authorizeRole } from '../middleware/auth';

const router = Router();

router.post('/', authorizeRole('admin'), createUser);

export default router;

Gelişmiş Routing Teknikleri ve Performans İpuçları Nelerdir?

Bir Express.js uygulamasını sadece çalışır hale getirmek bir başlangıçtır, ancak onu yüksek performanslı, güvenli ve sürdürülebilir kılmak gelişmiş teknikler gerektirir. Routing katmanınızın sağlamlığı, uygulamanızın genel performansını ve kullanıcı deneyimini doğrudan etkiler. Bu bölümde, Express ve TypeScript ile uygulayabileceğiniz bazı gelişmiş yönlendirme tekniklerine ve performans optimizasyonlarına odaklanacağız.

İlk olarak, asenkron işlemler ve hata yakalama Express uygulamalarında kritik bir rol oynar. Modern Node.js geliştirme, yoğunlukla async/await sözdizimiyle asenkron fonksiyonlar kullanır. Rota işleyicilerinizde veya middleware'lerinizde asenkron operasyonlar (veritabanı sorguları, ağ istekleri vb.) kullandığınızda, bu işlemlerin potansiyel hatalarını düzgün bir şekilde yönetmek çok önemlidir. Express varsayılan olarak async/await ile fırlatılan hataları otomatik olarak yakalamaz. Bu nedenle, her asenkron işlem için manuel try-catch blokları eklemek yerine, express-async-errors gibi kütüphaneler kullanabilir veya kendi özel hata sarıcı (wrapper) fonksiyonlarınızı yazarak bu hataları merkezi bir hata işleyici middleware'ine yönlendirebilirsiniz. Bu, kodunuzu daha temiz ve hata yönetimini daha tutarlı hale getirir.

Performans açısından, önbellekleme (caching) ve oran sınırlama (rate limiting) stratejileri, API'lerinizin yük altında daha iyi performans göstermesini sağlar. Route caching, sık erişilen ve değişmeyen verileri (örneğin, statik içerikler veya nadiren güncellenen ürün listeleri) belirli bir süre bellekte tutarak her istekte veritabanına gitme ihtiyacını ortadan kaldırır. Bu, yanıt sürelerini önemli ölçüde azaltır ve sunucu üzerindeki yükü hafifletir. Oran sınırlama ise, belirli bir zaman diliminde bir IP adresinden veya bir kullanıcıdan gelen istek sayısını kısıtlayarak API'nizi kötüye kullanıma (örneğin, DoS saldırıları veya kaba kuvvet denemeleri) karşı korur. express-rate-limit gibi kütüphaneler bu işlevi kolayca entegre etmenizi sağlar. Bu tür güvenlik ve performans önlemleri, özellikle halka açık API'ler için vazgeçilmezdir.

Ayrıca, mobil uyumlu API tasarımı, modern uygulamaların önemli bir parçasıdır. Gelen isteklerin mobil cihazlardan mı yoksa masaüstünden mi geldiğini tespit ederek, farklı cihazlara farklı yanıtlar (örneğin, daha az veri veya farklı formatta) gönderebilirsiniz. Bu, routing seviyesinde bir middleware ile kontrol edilebilir veya User-Agent başlığını inceleyerek yapılabilir. Her ne kadar yönlendirme doğrudan mobil uyumlu HTML üretmese de, API'nizin mobil ön uçlar için daha optimize edilmiş yanıtlar vermesi genel kullanıcı deneyimini iyileştirir. Aşağıda bir CSS medya sorgusu örneği verilmiştir, ancak API'nizin bu tür bir esneklik sunması, ön uç geliştiricilere daha fazla kontrol sağlar. Son olarak, API versiyonlama, API'nizin zaman içinde evrimleşmesini sağlar. URL tabanlı (/v1/users, /v2/users) veya Header tabanlı (Accept-version: 1.0) versiyonlama stratejileri, istemcilerinizin eski API sürümlerini kullanmaya devam ederken yeni özellikler eklemenize olanak tanır. Bu, büyük ve sürekli gelişen projeler için kritik bir yönetim stratejisidir.


// src/utils/asyncHandler.ts
import { Request, Response, NextFunction } from 'express';

// Asenkron hata yakalama sarmalayıcısı (wrapper)
const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise) =>
  (req: Request, res: Response, next: NextFunction) => {
    Promise.resolve(fn(req, res, next)).catch(next);
  };

export default asyncHandler;

// src/controllers/productController.ts (asyncHandler kullanımı ile)
import { Request, Response } from 'express';
import asyncHandler from '../utils/asyncHandler';

export const getProductById = asyncHandler(async (req: Request<{ id: string }>, res: Response) => {
  const { id } = req.params;
  // Simüle edilmiş veritabanı işlemi
  const product = await new Promise(resolve => setTimeout(() => {
    if (id === '1') {
      resolve({ id: '1', ad: 'Laptop', fiyat: 15000 });
    } else {
      resolve(null);
    }
  }, 100));

  if (!product) {
    return res.status(404).send('Ürün bulunamadı.');
  }
  res.json(product);
});

// src/app.ts (veya router dosyası) içinde
import express, { Application, Request, Response, NextFunction } from 'express';
import { getProductById } from './controllers/productController';
import rateLimit from 'express-rate-limit'; // express-rate-limit kütüphanesi

const app: Application = express();
app.use(express.json());

// Oran sınırlama middleware'i
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 dakika
  max: 100, // Her IP için 15 dakikada 100 istek
  message: "Çok fazla istek gönderdiniz, lütfen bir süre sonra tekrar deneyin."
});

app.use('/api/', apiLimiter); // Tüm /api rotalarına oran sınırlama uygula

app.get('/api/products/:id', getProductById);

// CSS media query örneği (HTML içinde bir div'in stilini değiştirmek için)
// Bu sadece bir örnektir ve API'nin doğrudan responsive davranışı ile ilgili değildir,
// ancak mobil uyumlu tasarımı genel olarak destekleme anlamında verilmiştir.
/*

*/

// Hata yönetimi middleware'i (en sona konulmalı)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error('Yakalanan hata:', err.stack);
  res.status(500).send('Beklenmedik bir sunucu hatası oluştu.');
});

Sonuç: Geleceğe Yönelik Bir Bakış

Bu makalede, Express.js ve TypeScript'in güçlü kombinasyonunu kullanarak modern, ölçeklenebilir ve bakımı kolay API'ler oluşturmanın inceliklerini derinlemesine inceledik. Temel yönlendirme mekanizmalarından başlayarak, middleware'in gücünü, modüler bir routing mimarisinin faydalarını, TypeScript ile tip güvenli rotaların nasıl oluşturulacağını ve son olarak gelişmiş performans ve güvenlik tekniklerini ele aldık. Gördüğümüz gibi, doğru araçlar ve stratejilerle, karmaşık API'lerinizi bile düzenli, anlaşılır ve verimli bir şekilde yönetebilirsiniz. TypeScript'in tip güvenliği, geliştirme sürecindeki hataları en aza indirirken, Express.js'in esnekliği size projenizin gerektirdiği her türlü özelleştirmeyi yapma imkanı sunar.

Modern web uygulamaları sürekli evrim geçiriyor ve geliştiricilerden her zamankinden daha fazla esneklik ve verimlilik bekleniyor. Express ve TypeScript ile yönlendirme konusunda ustalaşmak, sadece bugünün değil, yarının da karmaşık API gereksinimlerini karşılamanıza yardımcı olacaktır. Bu teknikleri projelerinize uygulayarak, daha sağlam bir kod tabanı oluşturacak, ekip içi işbirliğini kolaylaştıracak ve sonuç olarak daha kaliteli ürünler ortaya koyacaksınız. Unutmayın, iyi tasarlanmış bir yönlendirme altyapısı, uygulamanızın gelecekteki büyümesini destekleyen temel bir yatırımdır. Şimdi bu bilgileri pratiğe dökme ve Express ve TypeScript ile API geliştirme becerilerinizi bir sonraki seviyeye taşıma zamanı.

Sıkça Sorulan Sorular (SSS)

  • Express ile TypeScript kullanmak performansı etkiler mi?

    Hayır, TypeScript derleme zamanında JavaScript'e dönüştürüldüğü için çalışma zamanı performansına doğrudan bir etkisi yoktur. Performans, yazdığınız JavaScript kodunun kalitesine ve kullandığınız kütüphanelere bağlıdır. TypeScript, hataları erken yakalayarak geliştirme sürecindeki zaman kaybını azaltır ve daha optimize edilmiş kod yazmanıza yardımcı olabilir.

  • Hangi durumlarda express.Router() kullanmalıyım?

    express.Router(), uygulamanızda belirli bir modül, kaynak veya API versiyonuna ait rotaları gruplandırmak istediğinizde kullanılmalıdır. Özellikle projeniz büyüdükçe veya farklı geliştiricilerin aynı anda farklı özellikler üzerinde çalıştığı durumlarda kodun modülerliğini, okunabilirliğini ve bakım kolaylığını artırmak için idealdir. Tüm rotaları tek bir dosyada tutmaktan kaçınmak istediğiniz her durumda tercih edebilirsiniz.

  • Büyük projelerde routing mimarisi nasıl olmalı?

    Büyük projelerde kaynak tabanlı (resource-based) ve katmanlı bir routing mimarisi önerilir. Her bir ana kaynak (kullanıcılar, ürünler vb.) için ayrı bir express.Router() örneği oluşturulmalı ve bu router'lar kendi controller'ları ve servis katmanlarıyla entegre edilmelidir. Ayrıca, middleware'leri uygun seviyelerde (uygulama geneli, router veya rota özelinde) kullanarak yetkilendirme, kimlik doğrulama, loglama gibi çapraz kesen endişeler yönetilmelidir. API versiyonlama da büyük projelerde esneklik sağlar.

  • Route validation için en iyi yöntem nedir?

    Route validation için en iyi yöntem, TypeScript'in tip sistemini Zod, Joi veya Yup gibi çalışma zamanı validasyon kütüphaneleriyle birleştirmektir. Bu kütüphanelerle şemalar tanımlayarak gelen istek verilerini (parametreler, sorgular, gövde) derleme zamanında tip açısından, çalışma zamanında ise içerik ve format açısından doğrulayabilirsiniz. Bu yaklaşım, hem tip güvenliği hem de sağlam veri işleme sağlar, hataları erken aşamada yakalar.

  • RESTful olmayan API'lerde routing nasıl yapılır?

    RESTful olmayan API'lerde (örneğin RPC tarzı veya GraphQL gibi) routing prensipleri biraz farklılık gösterebilir. Express.js hala HTTP metotları ve yollar aracılığıyla isteği ilgili işleyiciye yönlendirme yeteneği sunar. RPC tarzı API'lerde genellikle tek bir ana uç nokta (örneğin /api) kullanılır ve isteğin gövdesindeki bir alan (örneğin method) hangi fonksiyonun çağrılacağını belirler. GraphQL API'lerinde ise genellikle tek bir /graphql uç noktası bulunur ve tüm etkileşimler GraphQL sorguları veya mutasyonları aracılığıyla gerçekleşir. Her iki durumda da Express routing, gelen isteği doğru işleme katmanına ulaştırmak için temel bir köprü görevi görür.

Yorumlar
İçeriği beğendiniz mi? Bir tartışma başlatın veya görüşlerinizi paylaşın.
Yorum Yaz

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

E-posta Bülteni
Yazılım Topluluğuna Katılın
En son güncellemeleri, yaratıcı ipuçlarını ve özel kaynakları doğrudan e-posta kutunuza alın. Tasarım ve inovasyonun geleceğini birlikte keşfedelim.