Express.js Best Practices: Ölçeklenebilir Backend Mimarisi
Yazar: Burak Balkı | Kategori: DevOps | Okuma Süresi: 10 dk
Express.js ekosisteminde ölçeklenebilir, güvenli ve yüksek performanslı backend uygulamaları geliştirmek için kapsamlı best practices rehberi. Modern mimari,...
## Express.js Best Practices ve Modern Mimari Yaklaşımları
**Express.js**, Node.js ekosisteminin en popüler ve esnek web çatısıdır. Ancak bu esneklik, yanlış yapılandırıldığında ölçeklenebilirlik ve güvenlik sorunlarına yol açabilir. Bu rehberde, kurumsal düzeyde bir uygulama geliştirmek için gereken **Express.js best practices** standartlarını, performans optimizasyonlarını ve DevOps süreçlerini detaylandıracağız.
Uygulamanızın sürdürülebilir olması için sadece kod yazmak yeterli değildir; kodun nasıl organize edildiği, hataların nasıl yönetildiği ve güvenliğin nasıl sağlandığı kritik önem taşır. Modern backend dünyasında, bir uygulamanın başarısı verimlilik ve güvenliğe bağlıdır.
## Express.js Temel Kavramları ve Mimari Yapısı
Express.js temelde bir **Middleware (Ara Yazılım)** zinciridir. Her istek (request) ve yanıt (response) döngüsü, belirli fonksiyonlar silsilesinden geçer. Mimariyi anlamak için şu temel bileşenleri bilmek gerekir:
- **Application Instance:** Uygulamanın ana nesnesi.
- **Middleware:** İstek ve yanıt nesnelerine erişimi olan fonksiyonlar.
- **Routing:** URL yollarının belirli fonksiyonlara yönlendirilmesi.
- **Error Handling:** Hata durumlarının merkezi bir noktadan yönetilmesi.
### Modern Bir Başlangıç: Express.js Kurulumu
Profesyonel bir projeye başlarken bağımlılıkları yönetmek ve çevre değişkenlerini (environment variables) yapılandırmak ilk adımdır.
```javascript
// app.js - Temel Yapılandırma
const express = require('express');
const dotenv = require('dotenv');
dotenv.config(); // Çevre değişkenlerini yükle
const app = express();
// JSON gövdesini ayrıştırmak için dahili middleware
app.use(express.json({ limit: '10kb' }));
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Sunucu ${PORT} portunda çalışıyor.`);
});
```
## Gelişmiş Proje Klasör Yapısı ve Organizasyonu
Spagetti koddan kaçınmak için **Modüler Mimari** veya **MVC (Model-View-Controller)** yapısı tercih edilmelidir. DevOps süreçlerinde CI/CD boru hatlarının kodu kolayca test edebilmesi için mantıksal ayrım şarttır.
| Klasör | Açıklama |
| :--- | :--- |
| `/controllers` | İstek mantığını yöneten fonksiyonlar |
| `/models` | Veritabanı şemaları ve veri erişim katmanı |
| `/routes` | API uç noktalarının tanımları |
| `/middlewares` | Özel ara yazılımlar (Auth, Validation) |
| `/services` | İş mantığının (Business Logic) bulunduğu katman |
| `/utils` | Yardımcı fonksiyonlar ve sabitler |
| `/tests` | Unit ve Integration testleri |
### Rotaların Modüler Hale Getirilmesi
Kodun okunabilirliğini artırmak için rotaları ana dosyadan ayırın:
```javascript
// routes/userRoutes.js
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
router.route('/')
.get(userController.getAllUsers)
.post(userController.createUser);
module.exports = router;
```
## Middleware Kullanımı ve Özel Middleware Yazımı
Middleware'ler, Express.js'in kalbidir. Her middleware'in `next()` fonksiyonunu çağırması veya bir yanıt dönmesi gerekir. **Best practice** olarak, her middleware tek bir sorumluluğa (Single Responsibility Principle) sahip olmalıdır.
### Özel Logger Middleware Örneği
```javascript
// middlewares/logger.js
const logger = (req, res, next) => {
console.log(`${new Date().toISOString()} - ${req.method} ${req.url}`);
next();
};
module.exports = logger;
```
## Güvenlik Standartları ve Express.js Güvenlik Önlemleri
Express.js varsayılan olarak tüm güvenlik açıklarını kapatmaz. Uygulamanızı **OWASP** standartlarına uygun hale getirmek için şu adımları uygulamalısınız:
1. **Helmet.js Kullanımı:** HTTP başlıklarını güvenli hale getirir.
2. **Rate Limiting:** Brute-force saldırılarını engeller.
3. **Data Sanitization:** NoSQL Injection ve XSS saldırılarına karşı veriyi temizler.
4. **CORS Yapılandırması:** Sadece güvenilen kaynaklara izin verir.
### Güvenlik Katmanı Örneği
```javascript
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');
// HTTP başlıklarını koru
app.use(helmet());
// İstek limitini belirle
const limiter = rateLimit({
max: 100,
windowMs: 60 * 60 * 1000, // 1 saat
message: 'Çok fazla istek gönderildi, lütfen sonra tekrar deneyin.'
});
app.use('/api', limiter);
// Veri temizleme
app.use(mongoSanitize()); // NoSQL Injection koruması
app.use(xss()); // XSS koruması
```
## Performans Optimizasyonu: Hız ve Verimlilik Teknikleri
Node.js tek iş parçacıklı (single-threaded) olduğu için CPU yoğunluklu işlemler event loop'u bloke edebilir. Performansı artırmak için şu teknikler kullanılmalıdır:
- **Gzip Sıkıştırma:** Yanıt boyutunu küçültür.
- **Caching:** Redis gibi araçlarla sık erişilen verileri önbelleğe alın.
- **Clustering:** Çok çekirdekli işlemcilerden yararlanın.
- **Promise.all:** Bağımsız asenkron işlemleri paralel çalıştırın.
### Sıkıştırma ve Caching Uygulaması
```javascript
const compression = require('compression');
// Yanıtları Gzip ile sıkıştır
app.use(compression());
// Basit bir Cache Middleware mantığı
const cache = (duration) => (req, res, next) => {
// Burada Redis veya bellek içi cache kontrolü yapılabilir
next();
};
```
## Hata Yönetimi (Error Handling) ve Merkezi Kontrol
Uygulama genelinde `try-catch` bloklarını her yere yaymak yerine, merkezi bir hata yönetimi mekanizması kurun. Bu, kodun temiz kalmasını sağlar.
### Asenkron Hataları Yakalama
```javascript
// utils/catchAsync.js
module.exports = fn => {
return (req, res, next) => {
fn(req, res, next).catch(next);
};
};
// controllers/userController.js kullanımı
const catchAsync = require('../utils/catchAsync');
exports.getUser = catchAsync(async (req, res, next) => {
const user = await User.findById(req.params.id);
if (!user) return next(new AppError('Kullanıcı bulunamadı', 404));
res.status(200).json(user);
});
```
## Veri Doğrulama ve Sanitizasyon Stratejileri
İstek gövdelerini (request body) doğrulamak, veritabanı tutarlılığı için elzemdir. **Joi** veya **Zod** kütüphaneleri bu işlem için endüstri standardıdır.
```javascript
const Joi = require('joi');
const validateUser = (data) => {
const schema = Joi.object({
username: Joi.string().min(3).required(),
email: Joi.string().email().required(),
password: Joi.string().min(8).required()
});
return schema.validate(data);
};
```
## Log Yönetimi ve Monitoring Araçları
`console.log` production ortamı için yetersizdir. **Winston** veya **Pino** gibi loglama kütüphaneleri kullanarak hataları dosyalara veya merkezi log sunucularına (ELK Stack, Datadog) aktarın.
```javascript
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' }),
],
});
```
## Dockerize Express.js: DevOps Entegrasyonu
Uygulamanın her ortamda aynı şekilde çalışması için Docker kullanımı standarttır. İşte optimize edilmiş bir `Dockerfile` örneği:
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
```
## Sık Yapılan Hatalar
1. **Sync Fonksiyonlar Kullanmak:** `fs.readFileSync` gibi fonksiyonlar event loop'u durdurur. Her zaman asenkron versiyonları kullanın.
2. **Hata Yönetimini İhmal Etmek:** Yakalanmayan hatalar (uncaught exceptions) sunucunun çökmesine neden olur.
3. **Güvenlik Başlıklarını Unutmak:** Helmet kullanmamak uygulamayı XSS'e açık hale getirir.
4. **Gereksiz Paket Kullanımı:** Her küçük işlem için kütüphane eklemek paket boyutunu ve güvenlik riskini artırır.
## Sık Sorulan Sorular (FAQ)
**1. Express.js hala güncel mi?**
Evet, Express.js Node.js dünyasının en çok kullanılan framework'üdür ve devasa bir topluluk desteğine sahiptir.
**2. Neden `express-async-errors` kullanmalıyım?**
Express 4.x sürümünde asenkron hatalar otomatik olarak `next(err)`'e aktarılmaz. Bu paket veya özel bir wrapper (catchAsync) bu süreci otomatikleştirir.
**3. Body-parser kullanmalı mıyım?**
Express 4.16.0 sürümünden itibaren `express.json()` ve `express.urlencoded()` dahili olarak gelmektedir, harici pakete gerek yoktur.
**4. JWT (JSON Web Token) nerede saklanmalı?**
En güvenli yöntem, XSS saldırılarından korunmak için `httpOnly` ve `secure` flag'li çerezler (cookies) içinde saklamaktır.
**5. Uygulama canlıya alınırken hangi komut kullanılmalı?**
`node app.js` yerine, süreç yönetimi ve otomatik yeniden başlatma için **PM2** gibi bir process manager kullanılmalıdır.
## Özet ve Sonuç
Express.js ile profesyonel bir backend geliştirmek, sadece rotalar oluşturmaktan çok daha fazlasıdır. **Güvenlik**, **ölçeklenebilirlik** ve **hata yönetimi** prensiplerini uygulayarak, yüksek trafikli sistemlerde bile stabil çalışan uygulamalar inşa edebilirsiniz. Bu rehberde paylaşılan best practices yaklaşımları, modern DevOps süreçlerine uyumlu ve performans odaklı bir yapı kurmanıza yardımcı olacaktır.