Node.js ile Uygulama Geliştirme – 5 : REST Mimarisi ve RESTful Servisler Geliştirme [Uygulamamızın Genel Yapısı ]

Express framework e genel olarak baktığımız bir önceki yazıdan sonra, uygulamamızın genel yapısını inşa ederek devam edelim. Biz express generator kullanmak yerine kendi yapımızı biraz daha MVC ye benzer şekilde kendimiz kuralım. Bu yazıda oluşturduğumuz temel kurulum üzerine devam edelim.

ilk olarak uygulamamızın genel klasör/dosya yapısına bakalım;

dosyaYapisi

Gördüğümüz gibi,

  1. controller larımızı tutacagımız, controller klasörü,
  2. libs isimli uygulamanın genelinde kullancağımız sınıflarımızı ve konfigürasyonları tutacağımız bir klasör
  3. Veri modellerimizi tutacağımızın models klasörü
  4. uygulamamızın tamamı ve ya bir kısmı için kullanacağımız, middleware leri tutacağımız middleware klasörü
  5. statik dosyalarımızı tutacağımız, public klasörü den oluşan 5 ana klasörümüz var.
  6. Test klasörü de tahmin edebileceğimiz gibi test lerimi tutacağımız klasör.

.babelrc, package.json zaten mevuct olan dosyalarımız dı. Ayrıca uygulamamızın başlangıç noktası olan index.js dosyamız var. Sadece yeni olarak, error.js isimli bir sınıfımız var “genel hata yakalama işlemleri ” için kullanacağız.

Yukarıdaki ana yapının alt parçalarıda şu şekilde ;

altYapi

  1. Controller klasörümüz henüz bir controller içermediği için şuan boş.
  2. libs klasörü sırayla aşağıdaki parçalardan oluşmakta.
    1. boot.js – Uygulamamızın başlatılacağı dosya.(web we socket sunucularımızın başlatılacağı nokta.)
    2. config.js – uygulamamızla ilgili ayarları/konfigürasyonları taşıyan ilgili configürasyon dosyasını yükeleycek ana konfügürasyon dosyamız.
    3. config-dev, config-prod ve config-test isimli 3 dosyamiz, isimlerinden de anlasilacagi gibi sirasiyla gelistirme, dagitim ve tes ortami icin ilgili ayarlari barindiracaklar. config.js dosyamiz bunlardan birini otomatik olarak uygulamanin calistigi ortama gore yukleyecek.
    4. db.js sinifimiz ile veri tabani baglantilarimizi yonetecegiz.
    5. helper.js ise uygulamamizin tamaminda kullanacagimiz ve kndi yazacagimiz kucuk helper fonksiyonlari tutacak.
  3. middleware klasörü
    1. express framework ve uygulmamaız için kullanacağımız global middlewares
  4. models
    1. şimdilik 3 tane modelimiz var, ürün, ürün stok durumu ve kullanıcı modeli.

Yukarıdaki, dosya yapısını sadık kaldığımızı varsayarak, uygulamamızın ilk halini yazıp çalışır hale getirerek devam edelim. İlk olarak uygulamamızın başlangıç noktası olan “index.js ile başlayalalım.

index.js

import express from "express"  //express framework
import consign from "consign"; // module ordering modülü 

const app = express(); //express nesnemiz
//uygulamamız aksi belirtilmediği sürece, development ortamında çalışacak
app.set('env',process.env.NODE_ENV||'development')

//consing ile uygulamamızın iç hiyerarşisini organize ediyoruz.
consign()
    .include("libs/config.js") // ilk önce konfigürasyonu yükle
    .then("middlewares") // sonra middleware leri yükle
    .then("controllers") // sonra controlleri yükle
    .then("error.js") // hata denetim sınıfı yükle
    .then("libs/boot.js") // API sunucumuzu başlat
    .into(app); // yukarıdaki tüm modüllere  app değişkeni altında bir refarns tut.
     // Böylece uygulamamızda istediğimiz zaman kolayca ulaşabilelim

module.exports = app; 

Gördüğünüz gibi gayet sade ve basit bir başlangıç scripti. Yukarıdaki, consign  modülünün kullanımı teknik olarak “zorunlu” olmasada ciddi kolaylıklar sağlıyor. Eğer consign gibi bir yardımcı modül kullanmasaydık, yukarıdaki tüm parçaları (tek tek ) ve sıralamayı düzgün tutmak kaydıyla manuel olarak tanımlayacaktık. Ayrıca consing bir klasör altındaki tüm dosyaların otomatik olarak import edilmesi yada ilgili importlara referans tutulması gibi bir çok kolaylığıda berbaerinde sunmakta.

app.set('env',process.env.NODE_ENV||'development')

kısmı ile aksi belirtilmediğinde, uygulmamamız “development” modunda çalışacak. Başka bir değişle, uygulamamızı geliştirme sürceindeki ayarlarla kullancağız. Uygulamamızı dağıtıma çıakrttığımızda bu ayarı “production” olarak değiştireceğiz. Serinin devam eden kısımlarında NODE_ENV  ile igili kullanım ve örnek olacak.

module.exports = app; 

kısmı ise(klasik node.js export tanımı) consing ile import etmediğimiz kodlarımız için eğer gerek duyarsak bu başlangıç scriptine referans olarak kullanabileceğemiz bir “node.js export” tanımı. Consing kullandığımız için çoğu zaman bu referansa direkt olarak erişim ihtiyacı duymayacağız.  Şimdi ilk olarak konfigürasyon yönetimimiz ile devam edelim.

.include("libs/config.js") // ilk önce konfigürasyonu yükle

satırı ile “libs” klasörü altında “config.js” isimli bir dosyayı import ettik. Şimdi bu konfigürasyon dosyasımızı yazarak devame edelim. İlk olarak libs klasörü altında “config.js , config-dev.js ve config-prod.js” isimleriyle 3 tane dosya oluşturalım. Tahmin edeceğiniz gibi, config-dev “development” , config-prod ise “prorduction” ayarlarımızı tutacak. Sonra bşrde test ortamı için bir ekleme yapacağız. config.js ise çalışılan ortama göre doğru konfigürasyonu yükleyecek.

/libs/config.js

import devConfig  from './config-dev' // development ayarları
import prodConfig  from './config-prod' // production ayarları

module.exports = (app) => { // app nesnesi consign tarafından sağlandı!
    const env = app.get('env') || 'development'; // geliştirme ortamını sorgula
    let config = {};
    console.log("Config Env => ", env)

    if (env === 'development') { // eğer geliştirme ortamındaysak
        config = devConfig;
    }
    if (env === 'production') { // eğer production ortamındaysak.
        config = prodConfig
    }
    return config;  // konfigürasyonu döndür.
}

Hatırlarsanız config dosyamızı consign ile import ettiğimiz için, consign bize “app” değişkenin bir referansını otomatik olarak sağlıyordu.

module.exports = (app) => {...} 

Ana kongfigürasyon seçimini yapan bu sınıfımızdan sonra, kongfigürasyon dosyamızının kendisini yazalım. Şimdilik ihtiyacımız olan kısmı ile aşağıdaki gibi bir konfigürasyon dosyası yeterli olacaktır. İlerledikçe yeni ayarlar vs eklemeye devam edeceğiz.

/libs/config-dev.js

let config ={
    database:'yg',
    username: "yg",
    password: "123456",
    params: {
        dialect: "postgres", //'mysql'|'mariadb'|'sqlite'|'mssql' 
        host: 'localhost',

        define: {
            underscored: true
        }
    },
    tokenSecret:'c369775a-9a9b-4bbd-bd72-5a2294c76d82',
    expiresIn:'5h',
    port: process.env.PORT || 3000
}
module.exports = config

Yukarıdaki haliyle, ilk kısmında veri tabanı ayarlarımızı tutuyoruz. Ben localhost da çalışan “postgres” sunucusunu kullandığım için “dialect” kısmını “postgres” olarak tanımladım. Postgres sunucum üzerinde, ismi “yg” olan bir veri tabanım var ayrıca kullanıcı adı da “yg” ve son olarak da veritabanı şifrem.  Production / dağıtım ortamına çıkınca bu ayarları daha güvenlı bir şekilde tutacağız ama geliştirme ortamı için yeterli bir saklama şekli.

tokenSecret ve expiresIn ise ileride sistemimiz için oluşturacağımız JWT(json web token) temelli authentication/login sistemimiz için kullanacağımız değerler. İlgili kısım geldiğinde bu iki değeri ne için ve nasıl kullanacağımıza bakacağız. Son olarak, port isimli alanımız, uygulamamızın geliştirme ortamında hangi  port üzerinde çalışacağını tutacak.

process.env ve NODE_ENV gibi terim ve konulara production/dağıtım konusuna geldiğimizde tekrar döneceğiz. 
Şimdilik kısaca şöyle diyebiliriz; Node.js in çalışma zamanı ile ilgili tuttuğu bilgileri(env vars / ortam değişkenleri de dahil)
uygulamız içinde kullanmak yada bu bilgileri Node.js e dışarıdan sağlamak için kullanılan değer/değişken veya kısa yollar.

Geliştirme ortamımız için ayarlarımız şimdilik bu kadar. İlerride ihtiyaö duydukça yeni eklemeler yaomaya devam edeğiz. Diğer konfigürasyonlarımız “test” ve “production” ı şimdilik boş bırakabiliriz. İlgili kısım geldiğinde onları ozaman hazırlaaycağız. Özellikle daığıtım ayarlarımızı daha güvenli bir formda tutmamız lazım o konu ayrıca ele alınmak zorunda. Şimdilik “development” ayarlarımız bize yeterli ve uygulamamızı yazmaya devam edebiliriz.

libs/ klasörü altında db.js ve helper.js isimli dosyalarımızı, models klasörü altındaki modellerimizi bir sonraki yazıda veritabanı modellerimizi oluştururken yazacağız. Şimdilik eksik kalan 2 şey. middlewares klasörü altındaki app.middlewares.js sınfımız ve boot.js i yazmak.  Boot.js uygulamızı boot/başlatacak olan dosya olduğu için en son yazağız. Bu dosyadan önce diğer tüm ihtiyaöların ve ayarların karşılanmış olması gerekmekteki uygulmamız sağlıklı bir şekilde başatılsın. 

middlewares/app.middlewares.js

 import path from 'path';
 import logger from 'morgan'; 
 import bodyParser from 'body-parser'; 
 import express from 'express''

module.exports = (app) => {
    app.set('views', path.join(__dirname, './views'));
    app.set('env', process.env.NODE_ENV || 'development' )
    app.set('view engine', 'hbs');
    app.use(logger('dev'));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: true }));
    app.use(express.static(path.join(__dirname, '../public')));

    app.use('*', (req,res) => res.json({message:"Api up and running!"}))
}

Bu dosya en önemli dosyalarımızdan biri. Hatırlarsanız, yukarıda, consign ile konfigürasyon dosyamızdan hemen sonra (diğer her şeyden önce) yüklemiştik. Bir başka değişle bu dosyadaki her kod sistemimizdeki herşeyden önce yüklenip çalışacaktır. Biraz daha detaylı bakacak olursak. app.set() uygulamamızın geneli için geçerli bazı ayarları atıyoruz. Bu ayarlar express framework için özel anlam ifade etmekteler;

app.set('env', process.env.NODE_ENV || 'development' )
app.set('views', path.join(__dirname, './views'));
app.set('view engine', 'hbs');

İlk olarak tahmin edebileceğiniz gibi,  uygulamamız ın çalışma ortamı için herhangi bir değişken tnaımlanmış mı ona bakıyoruz

app.set('env', process.env.NODE_ENV || 'development' )

eğer, bir tanımlama varsa bunu express e bildiriyoruz. (eğer hernagi bir değişken tanımlanmadıysa, “development” ortamında olduğumuzu varsayıyoz. ) Sonrasında, API sunucumucuz normalde sadece json veri sunmak için tasarlansada, gerek duymamız durumunda “sunucu taraflı rendering” de yapabilmek için iki küçük ayar daha yapıyoruz.

app.set('views', path.join(__dirname, './views'));
app.set('view engine', 'hbs');

express e view dosyalarımız için ana dizinde view isimli bir klasöre bakmasını söyleyerek başlıyoruz. Sornasında view engine tanımı ile devam ediyoruz. Her framework için genelde yaygın ve popüler bir view engine vardır(Razor,Blade,Jade vs.) express dünyasında da handlebars en popüler ve kolay kullanımlı olanların başında gelmekte. Bizde ‘view engine’, ‘hbs’ ile handlebars ı kullanacağımızı belirtiyoruz.

express ve node.js web uygulamarı geliştirme adına diğer aşternatiflerine nazaran muhtemelen en geniş ve zengin view engine ekosistemine sahip platform ve framework diyebiliriz. Bu adresten genel bir listeye bakabilirsiniz.

app.set() den sonra gelen app.use() çağrıları ise genel olarak middleware register dediğimiz ve uygulamamıza gelen her istekten önce çalışacak olan middleware leri kayıt ettiğimiz çağrılar. Bu middlewarelerinin çoğu bir node.js uygulaması için olmazsa olmaz diyebielcğeimiz ana parçalar. Bunlara eklenti gibi de bakabiliriz. Express içinde kullanmak adına bir çok eklenti/middleware bulmak mümkün. google da yada npm de yapacağınız bir arama ile çok zengin bir eklenti /middleware havuzu ile karşılaşacaksınız.

aşağıdaki middleware ler express in 4 sürümüne kadar, express le birlikte dahili gelmekteydi, 4 sürümümü ile birlikte bunlar ana paketten ayrıldı ve böylece daha esnek bir yapı ve seçim özgürlüğü oluşmuş oldu.

app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, '../public')));

logger isimli middleware geliştirme ortamı için logglama yapan bir eklenti. Sunucuya gelen her isteği console otomatik olarak yazdıracaktır.

bodyParser ise uygulamamıza gelen istekleri otomatik olarak bizim için belirli formatlara çevirecek ve

bodyParser.json() ile json encoding/decoding işlemleri otomatik olarak yapılacakö

bodyParser.urlencoded({ extended: true }) ile de form verilerine uygulamamız içinde kolayca ulaşabilmemizi sağlayacaktır. Son olarak ;

express.static(path.join(__dirname, '../public')

tanımı ile eğer ihtiyaç duyarsak, statik dosyaları(css,pdf vs.) “ana dizindeki public klasöründe” tutacağımızı expresse bildirmiş oluyoruz. Son satırdaki;

 app.use('*', (req,res) => res.json({message:"Api up and running!"}))

ile kendimiz basit bir middleware oluşturduk ve şimdilik hiç bir controller yada routing tanımı olmadığı için, uygulamamıza gelen bütün istekleri bu şekilde cevaplamış olduk.

Son olarak, boot.js i yazarak bitirelim. hatırlarsanız, consign ile en son yüklediğimiz dosya bu dosya idi. Herşey hazır tüm ayarlar yapıldı, middlewareler kayıt edildi ve artık uygulamamızı boot/başlatabiliriz.

/libs/boot.js

module.exports = app => {
    const config = app.libs.config;
    app.listen(config.port, () => {
        console.log(`Uygulamamiz ${config.port} nolu port uzerinde calismakta`)
        console.log(`Uygulama Calisma Modu : ${app.get('env')}`)

    })
};

boot sınıfımızda yaptığımız tek şey app/express nesnesinin listen() metodunu çağırmak. ilk paramtere olarak uygulamamızı çalıştırmak istediğimiz port u ikinci paramtre olarak da opsiyonel bir call-back fonksiyonu veriyoruz.

buraya kadar olan kısımıyla uygulamamızın başlangıç yapsı hazır ve terminalden,

npm start

komutu ile uygulamamızı çalıştırabiliririz. Uygulamamız çalıştığında alağıdaki terminal görüntüsünü alıyor olacağız;

calismaekrani

tarayıcınızdan yada postmen benzeri bir istemciden,; localhost:3000 adresine ulaşmaya çalışırsanız, aşağıdaki gibi bir görüntüyü elde etmiş olmanız lazım.

upandrunning

[fvplayer src=”https://s3.amazonaws.com/yazilimgunluguvideo/uygulama+yapisi.mp4"]

Uygulamamızı buraya kadar olan haliyle githubdan temin edebilirsiniz.

Bu haliyle github dan indirdikten  sonra, ana dizinde ;

npm install

komutuyla, uygulamamız için gerekli npm paketlerini indirip, sonrasında;

npm start

komutu ile uygulamızı test edebiliriz.

 

 

Bir sonraki yazıyla veri modellerimiz ve ilk controllerimizi yazarak devam edelim inş.

 

Leave a Reply

Your email address will not be published. Required fields are marked *