Node Event Module Detayları -Node Dersleri 2

Node.js ‘in Event modülünü öğrenmeden önce javascriptin event driven programming mantığını yani olay odaklı programlama mantığını anlamamız gerekiyor. Peki nedir bu olay odaklı programlama ?

Event Driven Programming

İsminden de anlaşıldığı gibi sistem içerisinde bir olay gerçekleştiğinde diğer elemanların o olaya göre tepki göstermesidir. Örnek verecek olursan DOM üzerinde bir butona tıklanması olayı gerçekleştiğinde bir işlem yapabiliriz. Böyle bir durumda sistem o butonu tanımlamamızı sağlayan bir bilgi ile birlikte sistemde olayın gerçekleştiğini yayar(emir). Birisinin ayağa kalkıp “ben x kişisiyim ve y kişisi bana tıkladı” diye bağırması gibi düşünebiliriz. Daha sonra bu bilgiye göre bir işlem yapılacaksa bu sinyali bekleyen bölüm işareti almış olur ve tetiklenir- harekete geçer. İşlemler bu şekilde çalışır. Şimdi node.js ‘in bize sunduğu “events” modülüne bir bakalım.

Events Modülü İle Olaylar Olaylar

Şimdi olay odaklı programlamanın nasıl çalıştığını biliyoruz. Peki bu javascriptte nerede kullanılıyor? Hemen hemen her yerde! Node.js ile bir siteye istek atarken, veya javascriptte DOM’u dinlerken, eşzamanlı bir işlem yürüteceğimiz zaman veya kendi belirlediğimiz koşullar oluştuğunda bir olay döngüsünü tetikleyebiliriz. İşte bu noktada Node.js bize “events” adında bir modül sağlıyor. Bu modül yardımıyla olay sinyalleri üretip, dinleyip manipüle edebiliyoruz.

const EventEmitter = require("events");

Yukarıdaki satıyla projemize events modülünü dahil etmiş olduk. Şimdi yeni bir emitter objesi oluşturalım. Oluşturduğumuz bu objeyle hem bir olay dinleyebiliriz hem de bir olay yayabiliriz :

const emitter = new EventEmitter();

Öncelikle bir olay döngüsü dinlemeyi(listen) yazalım. Bu şekilde bizim verdiğimiz isimde bir olay gerçekleştiği zaman ikinci parametre olarak verdiğimiz callback fonksiyonu çalışacak.

emitter.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
	console.log("listener called", arg);
});

Bu kod ile artık “messageLogged” adında bir olay gerçekleştiğinde konsola bu olayın gerçekleştiğine dair bir mesaj yazacağız. .on fonksiyonunu aslında onClick’ten tanıyoruz. Türkçeye çevirdiğimizde “click” olayı gerçekleşince gibi bir anlam çıkıyor. Birinci argümanda olayın adını verirken ikinci argüman da o olay gerçekleşince çalışacak fonksiyonu belirlemiş oluyoruz. Ayrıca bu fonksiyonun aldığı argüman da sinyal ile birlikte yayılan veriyi işaret eder.

Şimdi birisi bu olayı dinliyorsa birisi de bu olayı yaymalı. Elimizde hazır bir emitter objesi varken bu obje yardımıyla aynı isimde bir olay yayalım ve sonuca bakalım :

emitter.emit('messageLogged', {id : 1, url : 'url'});

.emit fonksiyonu da process içinde bir sinyali istediğimiz veriyle birlikte yaymamızı sağlıyor. Tahmin edeceğiniz üzere birinci argüman sinyalin adını belirtirken ikinci argüman yayılacak veriyi belirtiyor. Kodu yorumlarla birlikte genel olarak yazacak olursak :

//Buradaki tüm fonksiyonlar arrow function şeklinde yazılabilir.

const EventEmitter = require("events"); //Class names beginning UpperCase

const emitter = new EventEmitter();
//Emitting means signaling an event at application making noise

//Raise an listener
//Burada eklediğimiz event listener, uygulamada bu sinyal oluştuğunda bunu yakalar. Bu listener bağlamak için .on metodunu kullanıyoruz. Bu metodun birinci parametresi dinlemek istediğimiz event'ın adı ikinci parametresi ise sinyal yakalandığında çalıştırılacak signal handler fonksiyonu.
emitter.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
	console.log("listener called", arg);
});

//Raise an event
//Örnek olarak burada eventımıza bir isim veriyoruz. İlerleyen zamanlarda her log işlemi yapıldığında bu sinyalin uygulamada yankılanmasını yani emit edilmesini sağlayacağız.	
emitter.emit('messageLogged', {id : 1, url : 'url'}); // Best practice : Birden fazla argüman gönderilecekse JSON olarak gönder.
//emitter.emit('messageLogged', 1, "url"); =>Verdiğimiz sonraki parametreler bu event ile taşınacak parametrelerdir. Yani event argumentstir. 

//Real world örneklerinde doğrudan EventEmitter ile işlemler yapılmaz genel olarak bir sınıf yazılır ve bu sınıf içinde gerekli bütün eventler gönderilir veya yakalanır.

Buraya kadar her şey güzeldi. Ancak gerçek uygulamalar üzerinde çalışırken bu kadar basit yapılar kurulmuyor. İşin doğrusu EventEmitter doğrudan bir obje olarak bile kullanılmıyor. Javascriptte hemen her şeyin bir obje olduğunu biliyoruz. Bu yüzden “events” sınıfını miras alan(extend) bir sınıf yazıp bu şekilde kullanıyoruz.

Örnek olarak bir logger sınıfı yazmak istiyoruz diyelim. Bu sınıf yardımıyla istediğimiz zaman bir olayın tetiklenmesini ve tetiklendikten sonra uzaktaki bir url’ye log bilgisi göndermesini istiyoruz. Öncelikle logger.js adında bir dosya oluşturarak events sınıfını extend eden kendi sınıfımızı yazalım.

logger.js

const EventEmitter = require("events"); 
//const emitter = new EventEmitter();

var url = "http://mylogger.com/log";
//Classı EventEmitter dan türeterek bu sınıfın sahip olduğu bütün özellikelere sahip oluyoruz.
class Logger extends EventEmitter{
	log(message){
		console.log(message);
		this.emit('messageLogged', {id : 1, url : url});//burada emitter yerine this diyoruz çünkü artık biz de emitter objesiyiz.
	}
}
module.exports = Logger;

Burada bir events’i require ile eklediğimiz sınıf EventEmitter oldu. Daha sonra Logger sınıfını yazarken de bu sınıftan extend ettik. Daha sonra da bu Logger sınıfımıza bir mesaj argümanı alan bir log fonksiyonu tanımlıyoruz. Emit ederken artık EventEmitter demiyoruz çünkü this referansının işaret ettiği Logger sınıfı, EventEmitter sınıfının bütün özelliklerine zaten sahip. Bu yüzden this.emit ile istediğimiz bilgiyi paylaşabiliyoruz.

Şimdi bunu kullandığımız bir event.js dosyası oluşturalım :

event.js

const EventEmitter = require("events"); //Class names beginning UpperCase

 const Logger = require('./logger');
 const logger =  new Logger();
 
 //Export ettiğimiz loggerın EventEmitter objesi olduğunu extend ettiği için biliyoruz. Dolayısıyla bu obje üzerinde .on çağırabiliyoruz.
 logger.on("messageLogged", function(arg) { //Burada kullanılan arg e olabilir veya herhangi bir isim olabilir.
	console.log("listener called", arg);
});

 
 logger.log('message');

Evet artık kendi yazdığımız ve process boyunca kendi objesinin yaydığı sinyalleri dinleyebilecek bir sınıfımız hazır. Ayrıca bu sınıfı oluşturup bir de buna bir listener yani olay dinleyecek fonksiyonu da .on ile yazdık. daha sonra da log fonksiyonuyla bir olay yaydığımızda bu yakalanacak ve listener called yazısı ekrana basılacak.

Express Temel Konular

Kurulum

  • npm init –yes
  • npm i express

Bilinmesi Gerekenler

Her değişiklikte node index.js diye tekrar başlatmamak için nodemon paketini yüklüyoruz. npm i nodemon => node monitor

Environment Variable PORT => Productiona çıktığımız yerde port ataması dinamik olabilir. Bu yüzden aşağıdaki kodla portu gelirleyip öyle listen diyoruz:

process.env.PORT || 3000 => mevcut processdeki environment global değişkeninin içindeki portu görüyoruz.

HTTP Metodları

  • app.get()
  • app.post()
  • app.put()
  • app.delete()

Input Validation With Joi

Bu metodları implement ederken belli input validationları yapmamız gerekiyor. Bu doğrulama işlemini Joi paketiyle yapabiliriz. Bu paket validate fonksiyonunu kullanmak için bir json(body) ve bir schema alır. Bunları karşılaştırıp bir json döndürür. Bu dönen json objesinde error set edilmişse oradaki details[0].message kısmından hata mesajına ulaşabiliriz.

var schema = { name = Joi.string().min(3).required()};

Joi.validate(req.body, schema);

Ayrıca aradığımızı bulamadığımız durumda da şu şekilde 404 döndürüp fonksiyonu sonlandırabiliriz : return res.status(404).send(“Could not find “);

Requestle Gelen Body’i Okumak

POST ve PUT requestlerinde gelen body json olarak gelir. Bu body bilgisini okuyup parse edebilmek için bir middleware kullanmamız gerekir. Bu da expressin json fonksiyonuyla olur. Bu yüzden uygulamamızın en üstüne app.use(express.json()) ifadesini ekleriz. Daha sonra gelen body’leri okumak için de req.body.<item> şeklinde bir ifade kullanırız..

Requeste Response Döndürme

Son olarak da gelen isteklere karşı cevap göndermek için res.send(<Data>); fonksiyonunu kullanıyoruz.

Paket Yönetimi

Paketler İçin Modülleri Yükleme

  • npm init
    • npm init —yes
  • npm install

Paket Yükleme

  • npm i underscore

Underscore Kullanma

var _ = require(‘underscore’);

console.log(_.contains([1,2,3,4,5,], 2)) ⇒ True

Dependencies

  • npm i mongoose ⇒ Yüklediğimizde package.json dosyasının içinde dependencies kısmında ismi görüyoruz
  • node_modules kısmına baktığımızda bir sürü dosya yüklendiğini görüyoruz. Bunlar mongoose’ın bağımlılıklarıdır. Eski sürümlerde her paketin dependenciesi kendi altında yüklenirdi ama windowsun path konusundaki karakter sınırından ve çok karmaşık olmasından dolayı artık doğrudan node_modules altına yüklenir. Bir tek eğer versiyonlar farklıysa paketin kendi klasörü altına yüklenir.

Source Control

  • node_modules klasörünü sildiğimizde sorun olmaz. Çünkü package.json içinde bu bağımlılıklar bulunur ve npm i komutunu çalıştırdığımızda otomatik olarak yüklenir.

Semantic Versioning

  • “mongoose” : “^4.13.6” ⇒ Major.Minor.Patch Burada başta kullandığımız karakterle major versiyon değişmediği sürece diğer versiyonların en güncel halinin yüklenmesi gerektiğini belirtiyoruz. Eş anlamlısı 4.x Eğer bunun yerine ^ ⇒ ~ tilda işareti kullanılırsa bu major ve minor sabitken patchleri yüklemesi gerektiği anlamına gelir. Eğer hiç bir karakter eklenmezse bu tam olarak o versiyonun kullanılması gerektiğini gösterir.
  • npm list ⇒ Bütün bağımlılıkları görmeyi sağlar. npm list —depth= 0 ⇒ sadece uygulamanın bağımlılıkları görülür.

Installing Spesific Version Of A Package

  • npm i mongoose@2.4.2

Update Packages

  • npm outdated ⇒ Versiyonu geçmiş paketleri gösterir.
  • npm update ⇒ Sadece minor ve patch çıkışlarını günceller.
  • npm i -g npm-chech-updates ⇒ Bununla yeni bir paket yükledik ve artık yeni bir cmd komutumuz var = npm-chech-updates
  • npm-chech-updates ⇒ Komutunu çalıştırdığımız artık outdated paketler de major update leri almış oldu.
  • Bu komut ncu -u ⇒ paketleri günceller ve package.json dosyası da güncellenir.
  • Sonra da npm i komutunu çalıştırırarak yeni updated paketleri yükleyebiliriz.

Development Dependencies

  • npm i jshint —save-dev ⇒ Bu flag ile implement ettiğimizde development dependency olarak görünür ve productionda çıkmaz

Uninstalling Package

  • npm un mongoose

How To Publish Node Package

  • index.js ile dosyayı oluşturduk. Sonra npm init —yes çalıştırdık.
  • Eğer üyeliğimiz varsa npm login yoksa npm adduser komutlarını çalıştırıyoruz.
  • Giriş yaptıktan sonra paketimizi yayınlamak için unique bir isim kullanmamız gerekiyor.
  • Paketimizdeki isim unique bir isimse (package.json içindeki) npm publish dediğimizde yayınlamış oluyoruz.

Update Published Package

  • Yeni versiyonu güncellemek için npm update minor veya npm update major veya npm update patch kullanabiliriz. Ayrıca package json dosyasını güncelleyebiliriz
  • Sonra tekrar npm publish çalıştırıyoruz.