Express.js uygulamalarında kontrolcüler, isteğin işlenmesi ve yanıtın oluşturulması süreçlerini nasıl yönetir? Bu makale, sunucu tarafı mantığını düzenlemenin temelini oluşturan bu kritik bileşenleri baştan sona anlamanızı sağlayacak kapsamlı bir rehber sunuyor. Express.js ile daha temiz, bakımı kolay ve ölçeklenebilir API’ler geliştirmek için kontrolcülerin gücünü keşfetmeye hazır olun.
Günümüzde modern web uygulamaları ve RESTful API’ler geliştirirken karşılaşılan en büyük zorluklardan biri, kod tabanının karmaşıklığını yönetmektir. Özellikle de uygulamanız büyüdükçe, her isteğe yanıt veren mantığın tek bir yerde toplanması, kodun okunabilirliğini, bakımını ve test edilebilirliğini önemli ölçüde zorlaştırabilir. İşte tam da bu noktada, Express.js kontrolcülerinin önemi ortaya çıkar.
Peki, bir Express.js kontrolcüsü tam olarak nedir? Temel olarak, bir kontrolcü (controller), belirli bir istemci isteğini (request) işlemekten ve uygun bir yanıt (response) döndürmekten sorumlu olan bir fonksiyondur veya fonksiyonlar kümesidir. Genellikle “Model-View-Controller (MVC)” gibi mimari kalıplarla anılsa da, Express.js doğrudan bir MVC çatısı değildir. Ancak, kontrolcü kavramını benimseyerek uygulamalarımızı MVC benzeri bir modülerlikte organize edebiliriz. Bu, rotalarımızın sadece isteği alıp kontrolcüye yönlendiren basit yapılar olmasını sağlarken, tüm iş mantığının (veritabanı işlemleri, doğrulama, hata yönetimi vb.) kontrolcüler içinde veya kontrolcüler tarafından çağrılan diğer modüllerde yer almasına olanak tanır.
Kontrolcüler kullanmanın faydalarını daha yakından inceleyelim:
- Modülerlik ve Sorumluluk Ayrımı: Kontrolcüler, iş mantığını rotalardan ayırarak her bir birimin tek bir sorumluluğu olmasını sağlar. Rotalar, sadece gelen isteği belirli bir kontrolcü fonksiyonuna yönlendirirken, kontrolcü bu isteği nasıl işleyeceğini bilir. Bu sayede, kodunuz daha düzenli, anlaşılır ve yönetilebilir hale gelir. Örneğin, kullanıcılarla ilgili tüm işlemler
userController.jsdosyasında toplanabilir. - Bakım Kolaylığı: İş mantığı ayrı dosyalarda organize edildiğinde, bir hatayı ayıklamak veya yeni bir özellik eklemek çok daha kolaylaşır. Belirli bir hatanın hangi kontrolcüde meydana geldiğini hızla tespit edebilir veya bir özelliğin hangi kontrolcü fonksiyonunu etkilediğini görebilirsiniz. Bu, uzun vadede projenin sürdürülebilirliğini artırır.
- Yeniden Kullanılabilirlik: Aynı iş mantığı farklı rotalar tarafından kullanılabiliyorsa, bu mantığı bir kontrolcü içinde tanımlayıp birden fazla rotada çağırabilirsiniz. Bu, “Kendini Tekrar Etme (DRY – Don’t Repeat Yourself)” prensibine uyulmasına yardımcı olur ve kod fazlalığını önler.
- Test Edilebilirlik: Sorumlulukları ayrılmış kontrolcüler, birim testleri (unit tests) yazmayı kolaylaştırır. Her bir kontrolcü fonksiyonunu izole bir şekilde test edebilir, farklı girişler karşısında nasıl davrandığını kontrol edebilirsiniz. Bu, uygulamanızın kalitesini ve güvenilirliğini artırır.
- Geliştirici Deneyimi: Büyük ekiplerde çalışırken, herkesin aynı kod tabanında farklı özellikler üzerinde eş zamanlı olarak çalışması gerekir. Kontrolcüler, çakışmaları en aza indiren ve iş bölümünü kolaylaştıran net bir yapı sunar. Bu da genel geliştirici deneyimini iyileştirir.
Özetle, kontrolcüler Express.js uygulamalarınızda karmaşıklığı yönetmek, kodu daha okunabilir hale getirmek ve uzun vadede projenizi sürdürülebilir kılmak için vazgeçilmez bir araçtır. Bir sonraki bölümde, kontrolcüleri pratik olarak nasıl yapılandıracağımızı ve rotalarımıza nasıl entegre edeceğimizi adım adım inceleyeceğiz.
Express.js Uygulamanızda Kontrolcüleri Nasıl Yapılandırırsınız?
Kontrolcülerin teorik faydalarını anladığımıza göre, şimdi bu yapıyı Express.js projenize nasıl entegre edeceğinize odaklanalım. Uygulamanızda kontrolcüleri yapılandırmak, genellikle belirli bir dosya düzenini takip etmeyi ve rotalarınızı bu kontrolcülere bağlamayı içerir. İşte adım adım bir rehber:
Adım 1: Proje Yapısını Oluşturma
Modüler bir yapı için, genellikle controllers ve routes adında iki ana dizin oluştururuz. Proje kök dizininiz şöyle görünebilir:
my-express-app/
├── node_modules/
├── controllers/
│ └── userController.js
├── routes/
│ └── userRoutes.js
├── app.js
├── package.json
└── ...
app.js ana uygulama dosyanız olacak, userController.js kullanıcılarla ilgili iş mantığını barındıracak ve userRoutes.js ise kullanıcı rotalarını tanımlayacaktır.
Adım 2: Ana Express Uygulamasını Tanımlama (app.js)
app.js dosyasında Express uygulamasını başlatır, middleware'leri tanımlar ve rotaları uygulamanıza dahil edersiniz. Bu dosyada, farklı rota modüllerinizi içe aktarıp uygulamanıza bağlamanız gerekecek.
// app.js
const express = require('express');
const app = express();
const port = 3000;
// Body parser middleware'i
app.use(express.json());
// Rota dosyalarını içeri aktarın
const userRoutes = require('./routes/userRoutes');
// Rotaları uygulamaya bağlayın
app.use('/api/users', userRoutes); // Tüm /api/users isteklerini userRoutes'a yönlendir
// Ana sayfa rotası (isteğe bağlı)
app.get('/', (req, res) => {
res.send('Hoş Geldiniz! API belgelerine göz atın.');
});
// Uygulamayı belirtilen portta dinlemeye başlayın
app.listen(port, () => {
console.log(Sunucu http://localhost:${port} adresinde çalışıyor.);
});
Gördüğünüz gibi, userRoutes modülü /api/users path'i altında kullanılıyor. Bu, tüm kullanıcılarla ilgili isteklerin bu rota modülüne yönlendirileceği anlamına geliyor.
Adım 3: Kontrolcü Dosyasını Oluşturma (controllers/userController.js)
Şimdi sıra asıl iş mantığını barındıracak olan kontrolcü dosyamızı oluşturmaya geldi. controllers/userController.js içinde, her biri belirli bir işlemi gerçekleştirecek fonksiyonlar tanımlayacağız. Bu fonksiyonlar, Express rota işleyici imzasına ((req, res, next) => { ... }) uygun olmalıdır.
// controllers/userController.js
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
// Tüm kullanıcıları getiren kontrolcü fonksiyonu
exports.getAllUsers = (req, res) => {
res.status(200).json({
status: 'success',
results: users.length,
data: {
users
}
});
};
// Belirli bir kullanıcıyı ID'ye göre getiren kontrolcü fonksiyonu
exports.getUserById = (req, res) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) {
return res.status(404).json({
status: 'fail',
message: 'Kullanıcı bulunamadı'
});
}
res.status(200).json({
status: 'success',
data: {
user
}
});
};
// Yeni bir kullanıcı oluşturan kontrolcü fonksiyonu
exports.createUser = (req, res) => {
const newUser = req.body; // İstek gövdesinden yeni kullanıcı bilgilerini al
// Basit bir ID atama mekanizması
newUser.id = users.length > 0 ? Math.max(...users.map(u => u.id)) + 1 : 1;
users.push(newUser);
res.status(201).json({ // 201 Created durum kodu
status: 'success',
data: {
user: newUser
}
});
};
// Bir kullanıcıyı güncelleyen kontrolcü fonksiyonu (örnek olması için eklendi)
exports.updateUser = (req, res) => {
const id = parseInt(req.params.id);
const userIndex = users.findIndex(u => u.id === id);
if (userIndex === -1) {
return res.status(404).json({ status: 'fail', message: 'Kullanıcı bulunamadı' });
}
users[userIndex] = { ...users[userIndex], ...req.body };
res.status(200).json({
status: 'success',
data: {
user: users[userIndex]
}
});
};
Bu örnekte, kullanıcı verilerini basit bir dizi içinde saklıyoruz. Gerçek bir uygulamada burada veritabanı işlemleri (MongoDB, PostgreSQL vb.) yer alacaktır. Her bir fonksiyonu exports anahtar kelimesiyle dışa aktarıyoruz, böylece diğer modüller bu fonksiyonları kullanabilir.
Adım 4: Rota Dosyasını Oluşturma ve Kontrolcüye Bağlama (routes/userRoutes.js)
Son olarak, routes/userRoutes.js dosyasında Express Router'ı kullanarak rotaları tanımlayacağız ve bu rotaları ilgili kontrolcü fonksiyonlarına bağlayacağız.
// routes/userRoutes.js
const express = require('express');
const userController = require('../controllers/userController'); // Kontrolcüyü içeri aktar
const router = express.Router(); // Yeni bir router objesi oluştur
// Rotaları kontrolcü fonksiyonlarına bağlama
router
.route('/') // '/' path'i için
.get(userController.getAllUsers) // GET istekleri getAllUsers fonksiyonuna gider
.post(userController.createUser); // POST istekleri createUser fonksiyonuna gider
router
.route('/:id') // '/:id' path'i için
.get(userController.getUserById) // GET istekleri getUserById fonksiyonuna gider
.patch(userController.updateUser); // PATCH istekleri updateUser fonksiyonuna gider
module.exports = router; // Router'ı dışa aktar
Bu yapıda, app.js sadece bir aracı görevi görüyor ve isteği doğru rota modülüne yönlendiriyor. Rota modülü ise isteği alıp uygun kontrolcü fonksiyonuna iletiyor. Böylece, uygulamanızın farklı katmanları arasında net bir ayrım sağlanmış oluyor. Özellikle karmaşık projelerde, bu ayrım kodun yönetimini inanılmaz derecede kolaylaştırır.
Büyük projelerde, kontrolcülerin doğrudan veritabanı işlemleri veya harici servis çağrıları yapmasından kaçınılmalıdır. Bunun yerine, kontrolcülerinize servis katmanından (örneğin,
userService.js) bağımlılık enjekte edebilir ve iş mantığını bu servis katmanında tutabilirsiniz. Bu, kontrolcülerinizi daha "ince" tutarak test edilebilirliği ve sorumluluk ayrımını daha da artırır.
Kontrolcülerle Gerçek Dünya Senaryolarını Nasıl Yönetebiliriz? (Vaka Analizi)
Teorik bilgileri ve basit yapılandırmayı öğrendik, ancak kontrolcüler gerçek dünya uygulamalarında nasıl daha etkin bir şekilde kullanılır? Özellikle hata yönetimi, asenkron işlemler ve veri doğrulama gibi konular, kontrolcülerin tasarımında kritik rol oynar. Şimdi, basit bir e-ticaret uygulamasındaki ürün yönetimi senaryosu üzerinden kontrolcülerin gücünü inceleyelim.
Vaka Senaryosu: Bir e-ticaret API'si geliştirdiğinizi düşünün. Bu API'nin yeni ürün ekleme, ürün listeleme, ürün detaylarını görüntüleme gibi fonksiyonları olmalı. Özellikle yeni bir ürün eklerken, ürün adının, fiyatının ve stok miktarının doğru formatta girildiğinden emin olmamız, bir hata oluştuğunda kullanıcıya anlamlı bir geri bildirim vermemiz ve tüm bu işlemlerin asenkron olarak (veritabanı işlemleri nedeniyle) sorunsuz çalışmasını sağlamamız gerekiyor.
Problemler ve Kontrolcü Çözümleri
- Veri Doğrulama (Validation): Gelen istek gövdesindeki verilerin (ürün adı, fiyat vb.) geçerli olduğundan emin olmalıyız. Kontrolcü içinde doğrudan doğrulama yapmak yerine, bunu bir middleware'e devretmek daha iyi bir yaklaşımdır.
- Asenkron İşlemler: Ürün bilgilerini veritabanına kaydetmek gibi işlemler genellikle asenkrondur. Kontrolcülerimizin bu asenkron işlemleri düzgün bir şekilde yönetmesi gerekir.
- Hata Yönetimi: Veritabanı bağlantı sorunları, doğrulama hataları veya diğer beklenmedik durumlar ortaya çıktığında, uygulamanın çökmemesi ve kullanıcıya anlaşılır hata mesajları dönmesi önemlidir.
Şimdi bu senaryoyu ele alan bir productController.js dosyası ve ilgili rota modülünü inceleyelim:
controllers/productController.js
// controllers/productController.js
const products = []; // Basit bir ürün deposu, gerçekte veritabanı olur
// Asenkron işlemleri kolaylaştırmak için bir yardımcı fonksiyon
const catchAsync = fn => {
return (req, res, next) => {
fn(req, res, next).catch(next); // Hataları Express'in hata işleyicisine gönder
};
};
// Tüm ürünleri getiren kontrolcü
exports.getAllProducts = catchAsync(async (req, res, next) => {
// Gerçek bir senaryoda burada veritabanından ürünler çekilirdi
// Örnek olarak gecikme simülasyonu yapalım
await new Promise(resolve => setTimeout(resolve, 100));
res.status(200).json({
status: 'success',
results: products.length,
data: {
products
}
});
});
// Yeni ürün ekleyen kontrolcü
exports.createProduct = catchAsync(async (req, res, next) => {
// NOT: Veri doğrulama genellikle bir middleware'de yapılır.
// Burada basitçe kontrol ediyoruz.
if (!req.body.name || !req.body.price || !req.body.stock) {
// Hata nesnesi oluşturup next() ile hata işleyiciye gönder
return next(new Error('Ürün adı, fiyatı ve stok miktarı gerekli.', 400));
}
// Gerçek bir senaryoda veritabanına yeni ürün ekleme işlemi olurdu
// Örnek olarak gecikme simülasyonu yapalım
await new Promise(resolve => setTimeout(resolve, 200));
const newProduct = {
id: products.length > 0 ? Math.max(...products.map(p => p.id)) + 1 : 1,
name: req.body.name,
price: req.body.price,
stock: req.body.stock
};
products.push(newProduct);
res.status(201).json({
status: 'success',
data: {
product: newProduct
}
});
});
// Belirli bir ürünü ID'ye göre getiren kontrolcü
exports.getProductById = catchAsync(async (req, res, next) => {
const id = parseInt(req.params.id);
// Gerçek bir senaryoda veritabanından ürün çekilirdi
await new Promise(resolve => setTimeout(resolve, 50));
const product = products.find(p => p.id === id);
if (!product) {
// Özel bir hata nesnesi oluşturup next() ile hata işleyiciye gönder
return next(new Error('Ürün bulunamadı', 404));
}
res.status(200).json({
status: 'success',
data: {
product
}
});
});
Yukarıdaki örnekte dikkat etmemiz gereken birkaç önemli nokta var:
catchAsyncYardımcı Fonksiyonu: Express, asenkron fonksiyonlarda oluşan hataları otomatik olarak yakalamaz. Bu nedenle,async/awaitkullanan kontrolcü fonksiyonlarımızı bircatchAsyncsarmalayıcı içine alarak, oluşan tüm hatalarınext(err)ile global hata işleyicimize yönlendiriyoruz. Bu, her kontrolcüde tekrar tekrartry-catchblokları yazma yükünü ortadan kaldırır.- Veri Doğrulama:
createProductfonksiyonunda basit bir doğrulama örneği görüyorsunuz. Ancak, gerçek uygulamalarda Joi veya Express-validator gibi kütüphaneleri kullanarak middleware katmanında çok daha kapsamlı doğrulamalar yapmanız önerilir. Bu, kontrolcünüzü daha temiz ve sadece iş mantığına odaklanmış tutar. - Hata Nesneleri:
next(new Error('mesaj', durumKodu))şeklinde özel hata mesajları ve durum kodları içeren hata nesneleri oluşturarak, global hata işleyicinizin bu hataları yakalayıp uygun HTTP yanıtları dönmesini sağlayabilirsiniz.
routes/productRoutes.js
// routes/productRoutes.js
const express = require('express');
const productController = require('../controllers/productController');
// const productValidator = require('../middlewares/productValidator'); // Örnek middleware
const router = express.Router();
router
.route('/')
.get(productController.getAllProducts)
// .post(productValidator.createProduct, productController.createProduct); // Middleware ile doğrulama
.post(productController.createProduct);
router
.route('/:id')
.get(productController.getProductById);
module.exports = router;
Son olarak, app.js dosyanızda /api/products rotasını productRoutes'a bağlayarak bu yapıyı aktif hale getirirsiniz. Bu vaka analizi, kontrolcülerin sadece rota-iş mantığı ayrımı sağlamakla kalmayıp, aynı zamanda asenkronite ve hata yönetimi gibi karmaşık konuları daha düzenli bir şekilde ele almamıza nasıl yardımcı olduğunu göstermektedir.
Uygulamanızın sonunda (tüm rotalardan sonra) genel bir hata işleyici middleware tanımlamak, tüm kontrolcü veya middleware'lerden gelen hataları merkezi bir yerde yönetmenizi sağlar. Bu sayede, tutarlı hata yanıtları verebilir ve hassas bilgilerin istemciye sızmasını engelleyebilirsiniz. Örnek:
// app.js dosyasının en altında
app.use((err, req, res, next) => {
console.error('Hata yakalandı:', err.message);
res.status(err.statusCode || 500).json({
status: 'error',
message: err.message || 'Beklenmedik bir hata oluştu!'
});
});
Daha Okunabilir ve Bakımı Kolay Kontrolcüler İçin En İyi Pratikler Nelerdir?
Kontrolcüleri projenize dahil etmek, başlangıç için harika bir adımdır. Ancak, zamanla uygulamanız büyüdükçe, kontrolcülerinizin de karmaşıklaşmaması ve "şişman kontrolcü" (fat controller) sorununa dönüşmemesi için belirli en iyi pratikleri uygulamak önemlidir. Bu pratikler, kodunuzu daha okunabilir, bakımı kolay ve ölçeklenebilir hale getirecektir.
1. Tek Sorumluluk Prensibi (Single Responsibility Principle - SRP)
Her kontrolcü fonksiyonu yalnızca tek bir işlevi yerine getirmelidir. Örneğin, bir createUser kontrolcüsü sadece kullanıcı oluşturmaktan sorumlu olmalı, aynı zamanda kullanıcıları listeleme veya güncelleme gibi işlevleri içermemelidir. Bu, kodunuzu daha modüler ve anlaşılır kılar. Bir fonksiyonun birden fazla iş yapmaya çalıştığını fark ettiğinizde, onu daha küçük ve odaklanmış fonksiyonlara bölmeyi düşünün.
2. Hafif Kontrolcüler (Thin Controllers)
Bu, belki de en önemli pratiklerden biridir. Kontrolcülerinizin "hafif" olması, yani doğrudan iş mantığını (veritabanı işlemleri, karmaşık hesaplamalar, dış API çağrıları vb.) içermemesi gerektiği anlamına gelir. Bunun yerine, bu tür iş mantıklarını ayrı "servis" (service) veya "model" katmanlarına devretmelisiniz. Kontrolcünün ana görevi:
- İstek gövdesinden ve parametrelerinden veriyi almak.
- Gerekli servisleri çağırmak.
- Servislerden gelen yanıtı işlemek ve istemciye uygun bir HTTP yanıtı göndermek.
Bu yaklaşım, kontrolcülerinizi sadece bir "trafik polisi" gibi çalıştırır, bu da onların daha basit, test edilebilir ve yeniden kullanılabilir olmasını sağlar. Örneğin, bir kullanıcının şifresini güncelleyen bir kontrolcü, doğrudan veritabanı sorgusu yapmak yerine, userService.updatePassword(userId, newPassword) gibi bir servis fonksiyonunu çağırabilir.
3. Doğrulama (Validation) ve Kimlik Doğrulama/Yetkilendirme (Authentication/Authorization) için Middleware Kullanımı
Gelen isteğin gövdesindeki verileri doğrulamak (örneğin, bir e-posta adresinin geçerli formatta olup olmadığını kontrol etmek) veya kullanıcının yetkili olup olmadığını kontrol etmek, kontrolcülerin sorumluluğu değildir. Bu tür işlemleri, kontrolcü fonksiyonunuz çağrılmadan önce çalışan ayrı middleware fonksiyonlarına devretmelisiniz. Joi, Express-validator gibi kütüphanelerle veri doğrulama middleware'leri oluşturabilir, Passport.js ile kimlik doğrulama işlemini yönetebilirsiniz. Bu sayede kontrolcüleriniz sadece temiz ve doğrulanmış verilerle çalışır.
4. Tutarlı Hata Yönetimi
Uygulamanızın her yerinde tutarlı bir hata yönetimi stratejisi benimseyin. Yukarıda bahsettiğimiz catchAsync yardımcı fonksiyonu ve global hata işleyici middleware'i, bu tutarlılığı sağlamak için harika yöntemlerdir. Kontrolcülerinizde manuel try-catch blokları yerine, hataları next(err) ile merkezi hata işleyiciye yönlendirin. Bu, kullanıcılarınıza anlamlı hata mesajları sunmanızı ve hassas hata detaylarının açığa çıkmasını engellemenizi sağlar.
5. Parametre Doğrulama
Rota parametrelerini (req.params) veya sorgu parametrelerini (req.query) kullanırken, bunların beklediğiniz formatta veya türde olduğundan emin olun. Örneğin, bir ID parametresinin sayısal olması bekleniyorsa, kontrolcüye gelmeden önce bunun kontrol edilmesi faydalıdır. Bu, olası güvenlik açıklarını veya çalışma zamanı hatalarını önlemeye yardımcı olur.
6. Modüler Dosya Yapısı
Kontrolcülerinizi ve ilgili modülleri mantıklı bir şekilde organize edin. Örneğin, her kaynak (users, products, orders vb.) için ayrı kontrolcü dosyaları, rota dosyaları ve belki de servis dosyaları oluşturun. Bu, projenizin büyümesiyle birlikte gezinmeyi ve bakımı çok daha kolay hale getirir.
Ölçeklenebilir bir Express.js projesi için modüler bir klasör yapısı şöyle görünebilir:
src/
├── app.js
├── config/ # Ortam değişkenleri, veritabanı ayarları
├── controllers/
│ ├── authController.js
│ └── userController.js
│ └── productController.js
├── middlewares/ # Kimlik doğrulama, doğrulama, hata yakalama
│ ├── authMiddleware.js
│ └── validationMiddleware.js
│ └── errorMiddleware.js
├── models/ # Veritabanı modelleri (Mongoose şemaları vb.)
│ ├── User.js
│ └── Product.js
├── routes/ # Rota tanımları
│ ├── authRoutes.js
│ └── userRoutes.js
│ └── productRoutes.js
├── services/ # İş mantığı, veritabanı etkileşimleri
│ ├── userService.js
│ └── productService.js
└── utils/ # Yardımcı fonksiyonlar (catchAsync, APIError sınıfı vb.)
├── appError.js
└── catchAsync.js
Bu yapı, her bir bileşenin sorumluluğunu net bir şekilde ayırarak projenizin yönetimini basitleştirir.
Bu en iyi pratikleri uygulayarak, Express.js kontrolcülerinizi sadece işlevsel değil, aynı zamanda uzun vadede bakımı kolay ve sürdürülebilir hale getirebilirsiniz. Unutmayın, iyi yazılmış bir kod tabanı sadece bugün değil, gelecekteki değişiklikler ve genişletmeler için de sağlam bir temel oluşturur.
Sonuç: Express.js Kontrolcülerinin Gücünü Keşfedin
Bu kapsamlı rehber boyunca, Express.js kontrolcülerinin ne olduğunu, neden bu kadar önemli olduklarını ve modern API geliştirmenin temelini nasıl oluşturduklarını detaylı bir şekilde inceledik. Kontrolcüler, bir uygulamanın iş mantığını rotalarından ayırarak modülerlik, bakım kolaylığı, yeniden kullanılabilirlik ve test edilebilirlik gibi sayısız fayda sağlar. Gerçek dünya senaryoları ve en iyi pratikler üzerinden, veri doğrulama, asenkron işlemler ve hata yönetimi gibi karmaşık konuları kontrolcüler aracılığıyla nasıl daha etkin bir şekilde ele alabileceğimizi gördük.
Express.js, "unopinionated" (fikirsiz) bir framework olduğu için, kontrolcüleri nasıl yapılandıracağınız konusunda size esneklik tanır. Ancak, yukarıda bahsedilen prensipleri ve yapılandırma yaklaşımlarını benimsemek, projenizin büyüklüğü ne olursa olsun, temiz, düzenli ve ölçeklenebilir bir kod tabanı oluşturmanıza yardımcı olacaktır. Bu bilgileri pratik projelerinizde uygulayarak kendi deneyiminizi geliştirmeniz, Express.js'te usta bir geliştirici olma yolunda atacağınız en önemli adımlardan biridir.
Sıkça Sorulan Sorular (SSS)
Kontrolcüleri ne zaman kullanmalıyım?
Uygulamanızda birden fazla rota tanımlamaya başladığınızda veya rotalarınızın işleyişi karmaşıklaşmaya başladığında kontrolcüleri kullanmalısınız. Kontrolcüler, iş mantığını rotalardan ayırarak kodunuzu daha okunabilir ve yönetilebilir hale getirir. Basit tek sayfalık uygulamalarda bile, ilerideki genişlemeler için bir alışkanlık olarak kontrolcü kullanmaya başlamak faydalıdır.
Kontrolcüler ve Middleware arasındaki fark nedir?
Kontrolcüler, bir HTTP isteğini doğrudan işlemekten ve bir yanıt döndürmekten sorumlu olan ana iş mantığı parçalarıdır. Middleware ise, istek ve yanıt döngüsü sırasında belirli işlemleri gerçekleştirmek için kullanılan fonksiyonlardır. Örneğin, bir middleware isteği doğrulayabilir, kimlik doğrulamasını yapabilir, loglama yapabilir veya hata yakalayabilir ve ardından isteği bir sonraki middleware'e veya kontrolcüye aktarabilir. Yani, middleware "ön işleme" veya "son işleme" yaparken, kontrolcü isteğin ana "işlenmesini" yapar.
Tek bir kontrolcü dosyasında birden fazla kontrolcü fonksiyonu bulunabilir mi?
Evet, kesinlikle bulunabilir. Aslında, bu yaygın bir pratiktir. Genellikle, belirli bir kaynağa (örneğin, "Kullanıcılar" veya "Ürünler") ait tüm işlemler (oluşturma, okuma, güncelleme, silme - CRUD) tek bir kontrolcü dosyası içinde (örneğin, userController.js veya productController.js) ayrı ayrı fonksiyonlar olarak tanımlanır. Bu, ilgili mantığı tek bir yerde toplar ve kodun organizasyonunu kolaylaştırır.
Kontrolcülerde veritabanı sorguları yapmak doğru mu?
Teknik olarak yapabilirsiniz, ancak bu genellikle en iyi pratik olarak kabul edilmez. "Hafif kontrolcüler" prensibine göre, kontrolcüler iş mantığını doğrudan kendileri uygulamamalıdır. Veritabanı sorguları gibi karmaşık iş mantığı, genellikle ayrı bir "servis" veya "model" katmanına taşınmalıdır. Kontrolcü, sadece bu servis katmanını çağırarak veritabanı işlemlerini tetiklemeli ve servisten gelen yanıtı işlemelidir. Bu ayrım, kodunuzu daha modüler, test edilebilir ve sorumlulukları net hale getirir.
