NodeJS ve Express ile Backend Uygulaması Oluşturmak

Ercüment Laçın
10 min readOct 12, 2022

Bu makalede NodeJS ve Express kullanarak nasıl backend uygulaması oluşturabileceğimizi anlatacağım.

Bu makalede backend uygulamamızı NodeJS üzerinde inşa edeceğiz. Bu makaleyi hazırlarken NodeJS versiyonu olarak 14.17.3 kullanıyor olacağım farklı versiyonlarda hata alırsanız iletişime geçmekten çekinmeyin. Kendi NodeJS versiyonunuzu kontrol etmek için komut satırı üzerinden şu komutu çalıştırmanız gerekir node -v.

Projemizi oluşturmak istediğimiz dizine gidelim ve komut satırından npm init --y komutunu çalıştıralım. Bu sayede uygulamamız için bir template oluşturduk ve sonunda otomatik olarak package.json dosyası yaratılmış oldu ana dizinimizde. Oluşan package.json dosyası projemiz hakkındaki bilgileri içerir.

Oluşan package.json dosyası projemiz hakkındaki bilgileri içerir.

Dosya, uygulamanın giriş noktasının olarak index.js dosyası olduğunu tanımlar. Bu dosyadaki scripts objecesi üzerinde ufak bir değişiklik yapalım.

{
// ...
"scripts": { "start": "node index.js", "test": "echo \"Error: no test specified\" && exit 1"},// ...
}

Şimdi uygulamızın ilk versiyonu için index.js dosyasını ana dizinimizde yaratalım ve içerisine şimdilik şu kodu yazalım:
console.log('hello world')
Programı NodeJS aracılığıyla direk komut satırı üzerinden çalıştırabiliriz:
node index.js
Yada npm scripti ile çalıştırabiliriz npm start. Bu start scripti çalışacak çünkü package.json dosyasında bunu tanımlamıştık.

Simple Web Server

Uygulamamızı basit bir web server haline getirmek için index.js dosyamızı şu şekilde değiştirelim.

index.js

Uygulama çalıştığında konsolda şu mesaj gözükür:
Server running on port 3000

http://localhost:3000/ adresini tarayıcınız üzerinden ziyaret ederek uygulamamızın çıktısını görebiliriz.

http://localhost:3000/

Aslında sunucu URL’nin sonraki kısmından bağımsız olarak aynı şekilde çalışır. Ayrıca http://localhost:3000/foo/bar adresi de aynı içeriği gösterir.

Şimdi biraz koddan bahsedelim. Kod yeni bir web server oluşturmak için http modülü olancreateServer metodunu kullanır. Sunucunun http://localhost:3000/ adresine her HTTP isteği yapıldığında çağrılan sunucuya bir olay işleyici kaydedilir.

İsteğin sonucunda 200 status kodu, header Content-Type text/plain olarak ayarlanır sonra site içeriği Hello World! ayarlanarak yanıtlanır.

Son satırlar, 3000 numaralı porta gönderilen HTTP isteklerini dinlemek için app değişkenine atanan http sunucusuna bağlanır.

Bu makaledeki backend sunucusunun ana amacı, frontend için JSON formatında veri sunmak. Bu nedenle sunucumuzu JSON formatında kodlanmış bir not listesi döndürecek şekilde değiştirelim:

Yapmış olduğumuz değişikliklerin sunucuya yansıması için serverı durdurup (serverı durdurmak için komut satırında Cltr+C yapmaniz yeterli) yeniden başlatmamız gerekir ardından tarayıcı üzerinden sayfamızı yenileyelim.

Content-Type olarak application/json atamasi yaptık bu alıcıya verilen verinin JSON biçiminde olduğunu bildirir. notes dizisi, JSON.stringfy(notes) yöntemiyle JSON’a dönüştürülür.

Tarayıcımızı açtığımızda notes dizisinin JSON biçiminde geldiğini görüyoruz.

Express

Sunucu kodumuzu doğrudan NodeJS’in yerleşik http web sunucusuyla uygulamak mümkün. Ancak bu biraz külfetli hele ki uygulamanın boyutu büyüdüğünde.

Yerleşik http modülüyle çalışmak için, NodeJS ile sunucu tarafında geliştirme yapmayı kolaylaştıracak bir çok kütüphane geliştirilmiştir. Bu kütüphaneler genellikle daha iyi bir soyutlama amacı sağlarlar. Açık ara bu amaca yönelik en popüler kütüphane ise express kütüphanesidir.

Express’i kullanmaya başlamak için şu komut ile proje bağımlılığı olarak ekleyelim.

npm install express

Bağımlılık ayrıca bizim package.json dosyamıza eklenir.

{
// ...
"dependencies": {
"express": "^4.18.1"
}
}

Bağımlılığın kaynak kodu, projenin ana dizininde yer alan node_modules dizininde yer alır. Express’e ek olarak dizinde çok sayıda başka bağımlılıklar bulabilirsiniz. Bunlar express kütüphanesinin bağımlılıklarıdır endişe edilecek bir şey yok. Bunlara projemizin transitive dependency(geçişli bağımlılıkları) denir.

Web ve Express

Şimdi uygulamamıza geri dönelim ve bu değişiklikleri yapalım.

express’e ilk adım

Uygulamanın yeni sürümünü kullanabilmek için server yeniden başlatılmalı.

Uygulama pek değişmedi. Kodumuzun hemen başında expressi projemize dahil ediyoruz ve ardından express uygulamasını app değişkenine aktarıyoruz.

const express = require('express');const app = express();

Ardından uygulamamız için iki adet route tanımlıyoruz. İlki, uygulamanın ana dizinine yapılan HTTP GET isteklerini işlemek için kullanılan bir event handler tanımlar.

app.get('/', (request, response) => {  res.send('<h1>Hello World!</h1>');});

Event handler fonksiyonu iki adet parametre alır. İlk parametre, HTTP isteğinin tüm bilgilerini içerir. İkincisi yanıt parametresi, yapılan isteğe nasıl yanıt verileceğini tanımlamak için kullanılır. Kodumuzda istek, response nesnesinin send yöntemi kullanılarak yanıtlanır. Metodu çağırmak sunucunun respond yöntemine iletilen <h1>Hello World!</h1> stringini içeren bir yanıt gönderecek HTTP isteğine yanıt vermesini sağlar. Parametre bir string olduğundan express otomatik olarak Content-Type değerini text/html olarak yarlar. Yanıtın durum kodu varsayılan olarak 200'dür.

İkinci route uygulamanın notlar yoluna yapılan HTTP GET isteklerini işlemek için kullanılan bir event handler tanımlar.

app.get('/api/notes', (request, response) => {  response.json(notes);});

İstek, response nesnesinin json metoduyla yanıtlanır. Metodu çağırmak kendisine iletilen notes dizisini JSON biçimli bir string olarak gönderir. Express, Content-Type değerini application/json olarak otomatik olarak ayarlar.

Yalnızca NodeJS kullandığımız önceki sürümlerde verileri JSON.stringify yöntemiyle JSON biçimine dönüştürmemiz gerekiyordu.

response.end(JSON.stringify(notes))

Express ile bu artık gerekli değildir, çünkü bu dönüşüm otomatik olarak gerçekleşir.

JSON bir string ve notlara atanan değer gibi bir JavaScript nesnesi olmadığını belirtmekte fayda var.

nodemon

Uygulamanın kodunda değişiklik yaptığımızda değişiklikleri görebilmek için uygulamayı yeniden başlatmamız gerekiyor. Uygulamayı önce Cltr+C yazarak kapatıp ardından uygulamayı yeniden başlatarak çalıştırıyoruz. Nodemon, nodemon’un başlatıldığında dizindeki dosyaları izleyecek ve herhangi bir değişiklik olduğunda uygulamayı otomatik olarak yeniden başlatacaktır.

Nodemon’un şu komutla bir geliştirme bağımlılığı olarak yükleyelim:

npm install --save-dev nodemon

package.json içeriği de değişti:

{
//...
"dependencies": {
"express": "^4.18.1",
},
"devDependencies": {
"nodemon": "^2.0.20"
}
}

Geliştirme bağımlılıkları ile yalnızca uygulamanın geliştirilmesi sırasında ihtiyaç duyulan araçlara atıfta bulunuyoruz, örn. nodemon gibi uygulamayı test etmek veya otomatik olarak yeniden başlatmak için.

Uygulamamızı nodemon ile şu şekilde başlatabiliriz:

node_modules/.bin/nodemon index.js

Uygulama kodunda yapılan değişiklikler artık sunucunun otomatik olarak yeniden başlatılmasına neden oluyor. Backend sunucusu otomatik olarak yeniden başlasa bile, tarayıcının yine de manuel olarak yenilenmesi gerektiğini belirtmekte fayda var.

Komut uzun ve oldukça tatsız, bu yüzden package.json dosyasında bunun için özel bir npm betiği tanımlayalım:

{
// ..
"scripts": {
"start": "node index.js",
"dev": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
// ..
}

Artık sunucuyu geliştirme modunda şu komutla başlatabiliriz:

npm run dev

REST

Kök URL’miz www.example.com olduğunu varsayalım.

Bu durumda tanımlayıcısı 10 olan bir not kaynağının adresi www.example.com/api/notes/10 olur.

Tüm not kaynaklarının tüm koleksiyonunun URL’si www.example.com/api/notes olur.

Kaynaklar üzerinde farklı işlemler gerçekleştirebiliriz. Yürütülecek işlem HTTP eylemi ile tanımlanır:

Roy Fielding tarafından sağlanan tanıma göre, aslında bir REST API tanımlamadık.

Tek bir kaynak getirme

Bireysel notlar üzerinde çalışmak için bir REST arayüzü sunacak şekilde uygulamamızı genişletelim. İlk önce tek bir kaynağı getirmek için bir route oluşturalım.

Bireysel bir not için kullanacağımız benzersiz adres, sondaki sayının notun benzersiz kimlik numarasını ifade eder. Mesela notes/10 biçimindedir.

İki nokta üst üste sözdizimini kullanarak express’teki route’lar için parametreleri tanımlayabiliriz:

fetching a single note

Şimdi app.get(‘/api/notes/:id’, …) bizim için /api/notes/SOMETHING biçimindeki tüm HTTP GET isteklerini işleyecektir. Burada SOMETHING isteğe bağlı bir string değeridir.

Bir isteğin route’undaki id parametresine request nesnesi aracılığı ile erişilebilir.

const id = request.params.id;

Artık array metodlarından find ile birlikte parametreyle eşleşen bir id değerine sahip olan notu bulabiliriz. Ardından bulunan not isteği gönderene geri gönderilir.

Tarayıcımızda http://localhost:3000/api/notes/1 adresine giderek uygulamamızı test ettiğimizde tarayıcı boş bir sayfa görüntülediği için çalışmadığını fark ediyoruz. Bu, yazılım geliştiricileri olarak bizim için bir sürpriz değil ve hata ayıklama zamanı şimdi.

Kodumuza console.log ekleyelim ve taracımız ile http://localhost:3000/api/notes/1 adresini tekrar ziyaret edelim. Yine boş bir ekran göreceksiniz ama komut satırına baktığımızda şunu göreceksiniz:

Route’dan gelen id parametresi uygulamamıza iletilmesine rağmen find metodu ile birlikte eşleşen bir not bulunamıyor. Bunun sebebi aslında parametre olarak bize id string 1 olarak gelmesi yani ‘1’ ama notlara baktığımızda ise id tipinin bir integer olarak görüyoruz. Bu yüzden parametreden gelen id değerini integer haline getirmemiz gerekiyor.

JavaScript string to integer

Şimdi tek bir kaynağın çekilmesi işe yarıyor.

fetching a single note resource

Ancak uygulamamızda başka bir sorun var. Var olmayan bir id’ye sahip bir not ararsak, sunucu şu şekilde yanıt verir:

Döndürülen HTTP status kodu 200'dür. Bu, yanıtın başarılı olduğu anlamına gelir. content-length değeri 0 olduğundan ve aynısı tarayıcıdan doğrulanabildiğinden, yanıtla birlikte geri gönderilen hiçbir veri yoktur.

Bu davranışın nedeni, eşleşen bir not bulunamazsa not değişkeninin undefined olarak ayarlanmasıdır. Bu durumun sunucuda daha iyi bir şekilde ele alınması gerekiyor. Herhangi bir not bulunamazsa, sunucu 200 yerine 404 bulunamadı status koduyla yanıt vermelidir.

Kodumuzda aşağıdaki değişikliği yapalım:

Yanıta hiçbir veri eklenmediğinden, status değerini ayarlamak için status metodunu ve herhangi bir veri göndermeden isteğe yanıt vermek için end metodunu kullanırız.

Uygulamamız çalışır ve not bulunmazsa hata durum kodunu gönderir. Ancak, web uygulamalarının normalde var olmayan bir sayfasını ziyaret ettiğimizde yaptığı gibi, uygulama kullanıcıya gösterecek hiçbir şey döndürmez. Aslında tarayıcıda herhangi bir şey görüntülememize gerek yok çünkü REST API’leri programlı kullanım için tasarlanmış arayüzlerdir ve gereken tek şey hata status kodudur.

Aslında, varsayılan NOT FOUND mesajını geçersiz kılarak 404 hatasının gönderilme nedeni hakkında bir ipucu vermek mümkündür.

Kaynakları silme

Şimdi kaynakları silmek için bir route oluşturalım. Silme işlemi, kaynağın url’sine bir HTTP DELETE isteği yapılarak gerçekleşir:

Kaynağın silinmesi başarılı olursa yani not var ve kaldırılmışsa, isteğe 204 no content status koduyla yanıt veririz ve yanıtla birlikte hiçbir veri döndürmeyiz.

Kaynak mevcut değilse, bir DELETE isteğine hangi durum kodunun döndürülmesi gerektiği konusunda bir fikir birliği yoktur. Gerçekten, sadece iki seçenek var, biri 204 diğeri ise 404. Basitlik adına uygulamamız her iki durumda da 204 ile yanıt verecektir.

Postman

Peki silme işlemini nasıl test edeceğiz? HTTP GET isteklerini tarayıcıdan yapmak kolaydır. Silme işlemini test etmek için biraz JavaScript yazabiliriz ancak test kodu yazmak her durumda her zaman en iyi çözüm değildir.

Backend’in test edilmesini kolaylaştırmak için birçok araç mevcuttur. Bunlardan biri komut satırı programı curl. Ancak curl yerine, uygulamayı test etmek için Postman kullanımına bir göz atacağız.

Buradan Postman masaüstü istemcisini kuralım ve deneyelim:

Bu durumda Postman’i kullanmak oldukça kolaydır. URL’yi tanımlamanız ve ardından doğru istek türünü (DELETE) seçmeniz yeterlidir.

Backend sunucusu doğru yanıt veriyor gibi görünüyor. http://localhost:3000/api/notes/ adresine bir HTTP GET isteği yaparak 1 id numarasına sahip notun artık listede olmadığını ve silme işleminin başarılı olduğunu gösterir.

Uygulamadaki notlar sadece belleğe kaydedildiğinden, uygulamayı yeniden başlattığımızda not listesi orijinal durumuna dönecektir.

Veri alma

Ardından, sunucuya yeni notlar eklemeyi mümkün kılalım. Not ekleme, http://localhost:3000/api/notes/ adresine bir HTTP POST isteğinde bulunarak ve yeni not için tüm bilgileri request body JSON biçiminde göndererek gerçekleşir.

Verilere kolayca ulaşabilmek için app.use(express.json()) komutu ile kullanıma alınan express json-parser’ın yardımına ihtiyacımız var.

Event handler fonksiyonu request nesnesinin body özelliğinden verilere erişebilir.

json-parser olmadan body özelliği undefined olurdu. json-parser fonksiyonu, bir isteğin JSON verilerini alacak, onu bir JavaScript nesnesine dönüştürecek ve ardından route işleyicisi çağrılmadan önce bunu request nesnesinin body özelliğine ekleyecek şekilde çalışır.

Şu an için uygulama, alınan verileri konsola yazdırmak ve yanıt olarak geri göndermek dışında hiçbir şey yapmıyor.

Uygulama mantığının geri kalanını uygulamadan önce, Postman ile verilerin gerçekten sunucu tarafından alındığını doğrulayalım. Postman’de URL’yi ve istek türünü tanımlamanın yanı sıra, body’de gönderilen verileri de tanımlamamız gerekir:

Uygulama, istekte gönderdiğimiz verileri konsola yazdırır:

Uygulamanın verileri doğru şekilde aldığını öğrendikten sonra, isteğin işlenmesini tamamlamanın zamanı gelir:

Not için benzersiz bir id değerine ihtiyacımız var. Öncelikle maxId değişkenine 0 değerini atıyoruz. Ardından mevcut listede daha önce eklenmiş en az bir not varsa arasında en büyük id numarasını bulup maxId değişkenine atıyoruz. Yeni notun kimliği daha sonra maxId + 1 olarak tanımlanır. Bu yöntem aslında tavsiye edilmez, ancak yakında değiştireceğimiz için şimdilik bununla devam edeceğiz.

Geçerli sürümde hala HTTP POST isteğinin isteğe bağlı özelliklere sahip nesneler eklemek için kullanılabilmesi sorunu var. Content özelliğinin boş olamayacağını tanımlayarak uygulamayı geliştirelim. important ve date özelliklerine varsayılan değerler verilecektir. Diğer gereksiz tüm özellikler atılır:

Notlar için yeni id numarası oluşturma mantığı, ayrı bir generateId fonksiyonuna aktarıldı.

Alınan verilerde content özelliği için bir değer yoksa sunucu isteğe 400 hatalı istek status koduyla yanıt verir.

return çok önemli burada, eğer ki satır 13'te return kullanmasaydık kod sonuna kadar yürütülür ve hatalı şekilde biçimlendirilmiş not uygulamaya kaydedilirdi.

Content özelliğinin bir değeri varsa not, alınan verilere dayalı olacaktır. Daha önce belirtildiği gibi, tarayıcıyı çalıştıran ana makinenin saatinin doğru ayarlanmış olduğuna güvenemeyeceğimiz için, sunucuda zaman damgaları oluşturmak tarayıcıdan daha iyidir. date özelliğinin oluşturulması artık sunucu tarafından yapılır.

important özelliği eksikse, değeri varsayılan olarak false olarak ayarlayacağız. Varsayılan değer şu anda oldukça tuhaf görünen bir şekilde oluşturuluyor:

important: body.important || false,

body değişkenine kaydedilen veriler important özelliğine sahipse ifade o değeri alacaktır. Özellik yoksa, dikey çizgilerin sağ tarafında tanımlanan ifade false olarak değerlendirilir.

Middleware

Daha önce kullandığımız express json-parser, bir middleware’dir. Middleware request ve response nesnelerini işlemek için kullanılabilen fonksiyonlardır.

Pratikte, aynı anda birkaç middleware fonksiyonu kullanabilirsiniz. Birden fazlasına sahip olduğunuzda, express’de kullanıma alındıkları sırayla tek tek yürütülürler.

Sunucuya gönderilen her istek hakkında bilgi yazdıran kendi middleware’imizi yazalım.

Middleware üç parametre alan bir fonksiyondur:

Fonksiyon gövdesinin sonunda parametre olarak geçirilen bir next fonksiyon çağrılır. next fonksiyonu, bir sonraki middleware kontrolünü sağlar.

Middleware şu şekilde kullanılır:

app.use(requestLogger);

Middleware fonksiyonu, kullanıma alındıkları sıraya göre çağrılır. json-parser’ın requestLoggeröncesinde kullanıldığına dikkat edin, aksi takdirde logger request.body için çalışmayacaktır.

Route çağrılmadan önce middleware çalışsın istiyorsak, middleware fonksiyonun route event handler fonksiyonundan önce kullanılması gerekir.

Yukarıdaki kodda gördüğünüz üzere note oluşturmadan önce o content özelliğinin gönderilip gönderilmediğini gönderilmedi ise hata vermesini sağladık. content özelliği var ama daha önce bu content’e sahip bir not varsa tekrardan eklenmemesi için kontroller yazdık. Hem content özelliği var hem de daha önce o content değeri ile not oluşturulmadıysa bu sefer route’un event handler’ı çalışacak.

Yeni bir makalede görüşmek üzere.

kaynak: https://fullstackopen.com/en/

--

--