Node.JS Express-თან და Typescript-თან ერთად

დღესდღეობით საკმაოდ პუპულალური არჩევანია TypeScript-ის არსებობა JavaScript-ის პროექტებში რადგანაც გვაქვს რა შემოღებული ტიპები, ჩვენი კოდი ხდება ნაკლებად ბაგიანი, გვაქვს დამატებითი Feature-ები და ასევე შესანიშნავი autocomlete-ი.

შესაბამისად TypeScript საკმაოდ მძლავრი ხელსაწყოა დეველოპერის ხელსაწყოთა არსენალში.

მაგრამ, მაინც რა არის TypeScript?

TypeScript არის პროგრამირების ენა, რომელიც არის დაფუძნებული ჯავასკრიპტზე და რომელიც საბოლოო ჯამში მაინც ჯავასკრიპის სკრიპტად გარდაიქმნება რამეთუ ბრაუზერის ძრავებმა და ასევე Node-ის ძრავამ არ იცის TypeScript-ის კოდი შეუიარაღებელი თვალით როგორ წაიკითხონ და გადათარგმნონ მანქანურ ინსტრუქციებში.

🐶 Fun Fact: Microsoft-მა TypeScript იმიტომ შექმნა, რომ საკუთარ C# დეველოპერებს ჯავასკრიპტთან მუშაობა გაადვილებოდათ. ამიტომაც ტაიპსკრიპტი ძალიან გაქვს C#-ს.

იმისათვის, რომ TypeScript-ის კოდი გარდაიქმნას ჯავასკრიპტის კოდად TypeScript-ს აქვს საკუთარი compiler, რომელსაც აქვს უამრავი პარამეტრი, რომელიც შეგვიძლია "ვაწვალოთ" და მოვარგოთ ჩვენს პრეფერენციებს.

ჩვენს Node.JS - Express.JS პროექტში ტაიპსკრიპტის ინტეგრაციისათვის დაგვჭირდება ასევე პოპულალური პაკეტი Babel - რომლის მთავარი დანიშნულებაა ჯავასკრიპტის კოდის ტრანსფორმაცია ამა თუ იმ გზით(მაგალითად: ჩვენი ES6 კოდი, რომ გარდაიქმნას ყველა ბრაუზერზე მორგებულ კოდად - რომ ყველგან ისე მუშაობდეს ჩვენი კოდი როგორც გვაქვს ჩაფიქრებული, ასევე გვქონდეს backward compatibility)

ჩვენ ტაიპსკრიპტს გამოვიყენებთ Babel-ის მეშვეობით, რადგანაც მხოლოდ TypeScript-ის კომპილატორს აქვს რიგი ლიმიტაციები.

პროექტის შექმნა

მოდი შევქმნათ პატარა Express-ის პროექტი:

mkdir express-typescript
cd express-typescript
npm init
npm install express
npm install --save-dev typescript @types/express

ახლაკი შევქმნათ src/server.ts ფაილი ჩვენს პროექტში და შიგნით ჩავწეროთ შემდეგი სკრიპტი:

import express, { Request, Response } from "express";

const images = [
    'https://c.tenor.com/rK3k9EgLkhEAAAAC/steins-gate.gif',
    'https://c.tenor.com/wvZfA6FeOs0AAAAd/naruto-boruto.gif',
    'https://media3.giphy.com/media/pGlDpwgWTLgBi/giphy.gif',
    'https://c.tenor.com/stGMm1ODsGsAAAAC/anime-vinland-saga.gif',
    'https://i.pinimg.com/originals/ee/8f/ed/ee8fed71f21624f59205460b23820873.gif',
    'https://i.pinimg.com/originals/dd/9d/1b/dd9d1bef17c23fccf6f8224d7a70b766.gif',
]

const server = express()

server.get('/', async (_: Request, res: Response) => {
  
    const randomImgIdx = Math.floor(Math.random() * 100) % images.length

    res.send(`
    <html>
        <head>
            <title>Anime Images</title>
        </head>
        <body>
        <style>
            body {
                display: flex;
                justify-content: center;
                align-items: center;
                background-color: #0c112d;
            }

            img {
                border-radius: 10px;
            }
        </style>
        <a href="/" style="width: 50%">
            <img src="${images[randomImgIdx]}" style="width: 100%" />
        </a>
        </body>
    </html>
    `);
})


server.listen(4444, () => console.log("Server is listening at http://localhost:4444"))

ეს არის უბრალოდ პატარა ვებსაიტი, რომელიც რენდომ ანიმე გიფებს გაჩვენებს დარეფრეშებისას

TypeScript-ის კონფიგურაცია

ახლა კი ჩვენ გვჭირდება, რომ შევქმნათ TypeScript-ის კონფიგურაციის ფაილი შემდეგი ბრძანებით:

npx tsc --init

ჩვენ დაგვიგენერირდება default კონფიგურაცია, რომელიც ჩვენს პროექტს უნდა მოვარგოთ. რიგი ცვლილებების შემდგომ TypeScript-ის კონფიგურაცია tsconfig.json- შემდეგ სახეს მიიღებს:

{
  "compilerOptions": {
    "target": "es2016",                       /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "module": "commonjs",                     /* Specify what module code is generated. */
    "paths": {
      "*": ["src/*"]                          /* Specify a set of entries that re-map imports to additional lookup locations. */
    },
    "baseUrl": "./",                          /* Specify the base directory to resolve non-relative module names. */            
    "outDir": "./build",                      /* Specify an output folder for all emitted files. */
    "removeComments": true,                   /* Disable emitting comments. */
    "esModuleInterop": true,                  /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
    "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
    "strict": true,                           /* Enable all strict type-checking options. */
    "strictNullChecks": true,                 /* When type checking, take into account 'null' and 'undefined'. */
    "strictFunctionTypes": true,              /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
    "strictBindCallApply": true,              /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
    "strictPropertyInitialization": true,     /* Check for class properties that are declared but not set in the constructor. */
    "noImplicitThis": true,                   /* Enable error reporting when 'this' is given the type 'any'. */
    "alwaysStrict": true,                     /* Ensure 'use strict' is always emitted. */
    "noUnusedLocals": true,                   /* Enable error reporting when local variables aren't read. */
    "noUnusedParameters": true,               /* Raise an error when a function parameter isn't read. */
    "skipLibCheck": true                      /* Skip type checking all .d.ts files. */
  }
}

ახლა ჩვენ თუ გავუშვებთ შემდეგ ბრძანებას:

npx tsc

ეს ბრძანება ტაიპსკრიპტის კონფიგურაციის მიხედვით შექმნის build ფოლდერს, რომელიც უკვე node-ის მიერ წაკითხვადია.

გავუშვათ node-ით დაკომპილირებული სკრიპტი:

node build/server.js

და დავინახავთ, რომ ჩვენი აპლიკაცია მუშაობს:

მაგრამ, რეალურად ნახევარი საქმე გავაკეთეთ ჯერ, რადგანაც როდესაც შევცვლით ჩვენს ქოუდბეისს გვჭირდება, რომ ჩვენი TypeScript-ის ქოუდბეისი ხელახლა დაკომპილირდეს და ასევე დარეფრეშებისას ცვლილებაც დავინახოთ.

Babel-ის საჭიროება

ამის გაკეთება რეალურად ტაიპსკრიპტის კომპაილერითაც შეიძლება, მაგრამ რაღაც ნაკლი აქვს ტაიპსკრიპტს. მაგალითისათვის, თუ ჩვენი ქოუდბეისი შეიცავს არა .ts ფაილებსაც, მაგალითად swagger.yaml, html და css ფაილები და ა.შ. საბოლო build ფოლდერში მხოლოდ *.ts ფაილები მოხვდება.

ამისათვის საჭიროა, რომ გამოვიყენოთ Babel-ი. გვჭირდება, რიგი package-ები:

  • @babel/core - ძირეული babel-ის ფუნქციონალისთვის

  • @babel/cli - CLI, რომლითაც babel-ს ვეტყვით რა უნდა გააკეთოს

  • @babel/preset-env - პრესეტი, რომლის მეშვეობითაც ვეტყვით როგორ გადააკომპილიროს კოდი

  • babel-plugin-module-resolver - პლაგინი, რომლის საშუალებითაც შესაძლებელია გვქონდეს absolute import-ები

  • @babel/preset-typescript - პრესეტი, რომელიც იქნება შუამავალი babel-ს და ტაიპსკრიპტს შორის

დავაინსტალიროთ ყველა ეს ჩამოთვლილი ფექიჯი(ყველა არის dev dependency):

npm install --save-dev @babel/core
npm install --save-dev @babel/cli 
npm install --save-dev @babel/preset-env 
npm install --save-dev babel-plugin-module-resolver
npm install --save-dev @babel/preset-typescript

და ასევე შევქმნათ პროექტის root-ში babel-ის კონფიგურაციის ფაილი, რომელსაც დავარქმევთ babel.config.json-ს.

შიგნით კი შემდეგი კონფიგურაცია გავუწეროთ:

{
    "presets": [
        "@babel/preset-typescript",
        [
            "@babel/preset-env", {
            "targets": {
                "node": 16
                }
            }
        ]
    ],
    "plugins": [
        [
            "module-resolver", {
                "root": ["./src"],
                "extensions": [".ts"]
            }
        ]
    ]
}

ამის შემდგომ package.json-ში ჩავამატოთ რამოდენიმე სკრიპტი, იმისათვის, რომ მარტივად გავუშვათ ჩვენი პროექტი საჭირო ვარიანტში:

{
  "scripts": {
    "start": "node build/server.js",
    "build:watch": "babel src --out-dir build --extensions .ts --copy-files --watch",
    "build:prod": "babel src --out-dir build --extensions .ts --copy-files --minified"
  },
 ...
}

ახლა თუ გავუშვებთ ბრძანებას:

npm run build:watch

ამ შემთხვევაში როცა კი რაიმე ცვლილებას შევიტანთ კოდში მაშინვე გადაკომპილირდება build ფოლდერში.

საბოლოო ცვლილებები

მაგრამ ამასთანავე გვჭირდება,რომ nodemon-ით გავუშვათ საბოლოო ჯავასკრიპტის ბილდი, რადგანაც ყოველ ცვლილებაზე nodemon-მა თავისით გადაარესტარტოს ჩვენი node-ის სერვერი.

მაგრამ რეალურად გამოდის, რომ გვჭირდება გვქონდეს გაშვებული ორი პროცესი:

  • babel - რომელიც აკონვერტირებს ჩვენს TypeScript-ის ქოუდბეიზს JavaScript-ის build ვარიანტად

  • nodemon - რომელიც build ფოლდერში გვაქვს გაშვებული და ნებისმიერ ცვლილებაზე არესტარტებს ჩვენს node-ის სერვერს, რომ ახალი ცვლილებები აღიქვას

შეგვიძლია, რომ ეს პროცესები ცალ-ცალკე გავუშვათ(ცალ-ცალკე ტერმინალის სესიაში) მაგრამ ისიც შეგვიძლია, რომ ერთდროულად გავუშვათ concurrently ფექიჯის დახმარებით 👊

ეს ორი ფექიჯიც დავაინსტალიროთ:

npm install --save-dev nodemon concurrently

და ასევე package.json-ში ერთი სკრიპტიც ჩავამატოთ:

{
...
"scripts": {
    ...
    "dev": "concurrently \"npm run build:watch\" \"nodemon build/server.js\"",
 }
}

ახლა თუ გავუშვებთ npm run dev-ს უკვე ჩვენი დეველოპმენტ გარემო მზად იქნება, Voilla 😇

  • npm run build:prod - უნდა გავუშვათ, როდესაც პროდაქშენის ვერსია გვინდა დავბილდოთ

  • npm start - უნდა გავუშვათ პროდაქშენზე იმის შემდგომ რაც გავუშვებთ npm run build:prod -ს

აბა თქვენ იცით, ღმერთი თქვენსკენ 🐶

Last updated