Express Uygulamasını TypeScript Kullanarak Geliştirmek

Selamlar.

Farklı teknolojilerle olan bağımı koparmamak adına belli aralıklarla o teknolojileri kullanıyorum. Bazen database bağımlı bazense database bağımsız oluyor bu uygulamalar. Biliyorsunuz TypeScript JavaScript’e derlenen bir JavaScript superset’i. Yani kendisi bağımsız ayrı bir dil değil.

Neyse express ise bir sunucu tarafında çalışan nodejs framework’ü. Şimdi nasıl bi proje yapım oluyor ona bakalım isterseniz. Öncelikle bağımlılıkları şöyle kurdum;

Bağımlılıkları Kuralım Hihihi (Dediye Sahip Çıkalım Gibi Hayal Edin)

npm i express pug ts-node typescript @types/express @types/node

Bunlar benim bağımlılıklarım.

package.json ve tsconfig.json Dosyasını Düzenleyelim

Daha sonra package.json dosyamda yer alan scripts bölümüm şöyle olacak;

"scripts": {
    "dev": "ts-node src/server.ts",
    "start": "ts-node dist/server.js",
    "build": "tsc -p ."
}

Yani npm run dev dediğimde ts-node kullanarak uygulamanın dev versiyonunu ayağa kaldırmış olacağım (:

Bir de typescript için tsconfig.json dosyam şöyle oluyor

{
    "compilerOptions": {
        "sourceMap": true,
        "target": "es6",
        "module": "commonjs",
        "outDir": "./dist",
        "baseUrl": "./src"
    },
    "include": [
        "src/**/*.ts"
    ],
    "exclude": [
        "node_modules"
    ]
}

Proje Hiyerarşisi Şöyle

Projede dizin yapım şöyle;

- dist
- node_modules
- public
- src
- views
package.json
tsconfig.json

Burada node_modules klasörü harici views ve public klasörleri de aslında nasıl çalışacağınıza bağlı. Yani UI bu proje tarafında olmayacaksa çok şart değil.

Proje src klasörü altında geliştirilecek. Bu dizin şöyle bi yapıya sahip olacak;

- controllers
- interfaces
- middleware
app.ts
server.ts

Controller klasörü o controller’a ait belirli interface’lere sahip olacak zaten. Diğer interfaces klasörü ise genel bir interface olacak. Son olarak middleware ise oluşturulan middleware’lar için.

Gelelim app.ts ve server.ts dosyalarına. Burada app.ts genel application sınıfımızı taşıyacak. Yani bir kere oluşturduktan sonra çok lazım olmadıkça değişmeyecek. Ancak server.ts her controller eklememize değişecek (:

Uygulama Dosyasına Bakalım

app.ts

Bu dosyayı ben şöyle yaptım;

import * as express from 'express'
import { Application } from 'express'

class App {
    public app: Application
    public port: number

    constructor(appInit: { port: number; middleWares: any; controllers: any; }) {
        this.app = express()
        this.port = appInit.port

        this.middlewares(appInit.middleWares)
        this.routes(appInit.controllers)
        this.assets()
        this.template()
    }

    private middlewares(middleWares: { forEach: (arg0: (middleWare: any) => void) => void; }) {
        middleWares.forEach(middleWare => {
            this.app.use(middleWare)
        })
    }

    private routes(controllers: { forEach: (arg0: (controller: any) => void) => void; }) {
        controllers.forEach(controller => {
            this.app.use('/', controller.router)
        })
    }

    private assets() {
        this.app.use(express.static('public'))
        this.app.use(express.static('views'))
    }

    private template() {
        this.app.set('view engine', 'pug')
    }

    public listen() {
        this.app.listen(this.port, () => {
            console.log(`App listening on the http://localhost:${this.port}`)
        })
    }
}

export default App

Burada controller ve middleware gruplarım array olarak değer alıyorlar. Ayrıca UI tarafının da işin içinde olacağını düşündüğüm için assets ve templatelerimi de init ettim. Buna rağmen hala kullanmak zorunda değilsiniz.

Server Dosyasına Bakalım

Sonra server.ts dosyam ise şöyle;

import App from './app'

import * as bodyParser from 'body-parser'
import loggerMiddleware from './middleware/logger'

import PostsController from './controllers/posts/posts.controller'
import HomeController from './controllers/home/home.controller'

const app = new App({
    port: 5000,
    controllers: [
        new HomeController(),
        new PostsController()
    ],
    middleWares: [
        bodyParser.json(),
        bodyParser.urlencoded({ extended: true }),
        loggerMiddleware
    ]
})

app.listen()

Fark ettiyseniz controller’ım ayrı bir yerde, middleware’larım ayrı bir yerde geliyor. Bu iki grup da array türünden değer alıyor. Örnek 2 adet controller’ım var. Ve kendi oluşturduğum örnek bir middleware’ım var. Önce ona bakalım en son controller içerisine girelim.

Middleware Örneği Bile Var!

middleware/logger.ts

import { Request, Response } from 'express'

const loggerMiddleware = (req: Request, resp: Response, next) => {

    console.log('Request logged:', req.method, req.path)
    next()
}

export default loggerMiddleware

Basitçe yaptığı iş gelen request’in HTTP verb’ünü ve path’ini yazdırmak. Bu örnek olsun diye yazdığım bir middleware. Sonra gelelim interfaces klasörü içerisine.

Genel Interface Dosyası Yaptım. Developerın Şimdi Görür!

Bu klasörde örnek için IControllerBase.interface.ts adında bir interface dosyam var. Kısacası controller oluşturunca bu interface’i implement ediyorum. O da mevcut durumda initRoutes() adında bir method barındırıyor.

interfaces/IControllerBase.interface.ts

interface IControllerBase {
    initRoutes(): any
}

export default IControllerBase

Eveeet geldik şimdi controller dosyalarına. Ben örnek olsun diye buraya sadece HomeController’ı koyacağım.

İlk Controller Dosyası. Artık Tüm İşi Bitirdim (Bitiremedi :P)

controllers/home.controller.ts

import * as express from 'express'
import { Request, Response } from 'express'
import IControllerBase from 'interfaces/IControllerBase.interface'

class HomeController implements IControllerBase {
    public path = '/'
    public router = express.Router()

    constructor() {
        this.initRoutes()
    }

    public initRoutes() {
        this.router.get('/', this.index)
    }

    index = (req: Request, res: Response) => {

        const users = [
            {
                id: 1,
                name: 'Ali'
            },
            {
                id: 2,
                name: 'Can'
            },
            {
                id: 3,
                name: 'Ahmet'
            }
        ]

        res.render('home/index', { users })
    }
}

export default HomeController

Kısacası elimde veri tabanından gelen bir array olduğunu düşündüm ve view altındaki home/index.pug dosyasına pasladım. View engine’i app.ts dosyasında ayarlamıştım.

Ayrıca IControllerBase interface’ini implement ettiğim için initRoutes metodunu da oluşturma zorunluluğuna sahibim artık (: Ona da endpointlerimden sadece birisini verdim.

Hadi Çalıştıralım

Artık npm run dev komutunu verince sistemi ayağa kaldırabilirim.

http://localhost:5000 adresinden yaptığımız eseri görüntüleyebiliriz :F

Diğer posts klasörünü de GitHub üzerinden görüntüleyebilirsiniz. Projeyi oraya attım. TypeORM ya da Sequlize tarzı ORM kullanarak gerçek veriler üzerinden de çalışabilirsiniz.

Özet Geç Kardeşim

Ben çok yormadan projenin GitHub linkini şurada paylaşayım size;

https://github.com/aligoren/express-typescript-test

Okuduğunuz için teşekkür ederim. Umarım size faydalı olabilecek ufak da olsa bilgiler içeriyordur. Öyle olmasını umuyorum. Hatalı, yanlış, eksik falan bir şey varsa çekinmeden yargılayın beni.

Tşk 😛