צעדים ראשונים עם node.js ו express

פוסט זה כולל טיפ קצר בנושא פיתוח Front End. אם אתם רוצים ללמוד יותר לעומק על פיתוח Front End מהבסיס ועד הנושאים המתקדמים תשמחו לשמוע שבניתי קורס וידאו מקיף בנושא זה הכולל מעל 50 שיעורי וידאו והמון תרגול מעשי.
למידע נוסף והצטרפות לקורס בקרו בדף קורס Front End באתר.
 

סביבת node.js היא סביבת ריצה המפעילה תוכניות JavaScript מחוץ לדפדפן. היום משתמשים בה לכתיבת סקריפטים וכלים קטנים, וגם לכתיבת תוכניות בצד השרת. בפוסט אציג ממשק צד-שרת להעברת הודעות באמצעות node.js דרך מספר דוגמאות.

1. דוגמא 1: שלום node

נתחיל עם דוגמת hello world קלאסית. אחרי שתתקינו node.js (אפשר להוריד בחינם מכאן) פתחו חלון שורת פקודה בתוך תיקיה חדשה שתיצרו ושם הריצו:

$ npm init -y

תוכניות JavaScript צריכות דרך לנהל תלויות בינן לבין תוכניות JavaScript אחרות. בדפדפן ה HTML אחראי לטעינת כל קבצי ה JavaScript הרלוונטים, כולל קבצים של ספריות חיצוניות. ב node.js קובץ בשם package.json שנמצא בתיקיית הפרויקט מציין באיזה ספריות חיצוניות וגרסאות שלהן הפרויקט שלנו תלוי. הפקודה שהרצתם יוצרת קובץ package.json ראשוני.

בתוכנית הראשונה לא נשתמש בספריות חיצוניות, אך זה הרגל טוב לקראת הדוגמאות הבאות. צרו קובץ בשם index.js וכתבו בו את התוכן הבא:

console.log('Hello World');

לאחר מכן חזרו לשורת הפקודה והריצו:

$ node index.js
Hello World

התוצאה אצלי ואני מקוה שגם אצלכם היא ההדפסה Hello World.

מוזמנים לראות את הקוד בפעולה במכונה הוירטואלית הבאה. כתבו node index.js כדי לראות את ההדפסה.

2. דוגמא 2: שרת Web שסופר ביקורים

הגיע הזמן להתקדם ולבנות את אתר האינטרנט הראשון שלנו ב node. האתר לא מתוחכם במיוחד ובסך הכל יספור כמה אנשים ביקרו בו עד היום וידפיס את המספר לכל מבקר. בשביל זה נפתח תיקיה חדשה, נפעיל בתוכה npm init ולאחר מכן נפעיל את הפקודה הבאה כדי להתקין את הספריה express:

$ npm install --save express

הפקודה עושה שני דברים: היא גם מתקינה את הספריה express בתוך תיקיה בשם node_modules וגם משנה את הקובץ package.json ומוסיפה אליו את התלות ב express.

ספריית express היא ספריית פיתוח צד השרת של node, והיא מספקת תחביר מאוד פשוט לפיתוח אתרים ו APIs. אחרי שהתקנתם את הספריה כתבו את התוכן הבא בקובץ index.js:

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

let counter = 0;

app.get('/', function (req, res) {
  counter += 1;
  res.send('Hello World!. Visitors count so far: ' + counter);
});

app.listen(3010, function () {
  console.log('Example app listening on port 3010!');
});

הקוד מגדיר מיפוי בין נתיבים לבין קוד שיופעל כשגולש יגיע לנתיבים אלו. כך כשגולש יגיע לנתיב / יופעל הקוד בפונקציה הראשונה, כלומר ערך המונה יעלה ב-1 ותישלח לגולש חזרה הודעת התשובה. הודעה זו זה מה שיופיע על מסך הדפדפן.

הפעילו את השרת באמצעות:

$ node index.js
Example app listening on port 3010!

אחרי שתקבלו את ההודעה תוכלו לגלוש באמצעות דפדפן האינטרנט שלכם לכתובת http://localhost:3010 ולקבל את ההודעה יחד עם ערך מונה המבקרים. אפשר לראות את התוצאה גם במכונה הוירטואלית הבאה:

3. דוגמא 3: מערכת הודעות

מכאן אפשר להמשיך ולהוסיף קוד JavaScript כדי להפוך את השרת למתוחכם יותר ויותר. בנוסף, במקרים רבים נרצה לשלב גם קוד צד לקוח (HTML, CSS ו JavaScript) שייטען בדפדפן כדי לאפשר לגולשים לתקשר בצורה מתוחכמת יותר עם השרת.

בדוגמא זו אנו מוסיפים שתי ספריות עזר ל express. הראשונה נקראת static (למעשה היא חלק מ express) והיא שמאפשרת להגיש קבצים סטטיים, כלומר קבצי HTML, CSS, JS תמונות וכן הלאה לדפדפן באופן אוטומטי מתוך תיקיה קבועה מראש. הספריה השניה נקראת body parser והיא שמאפשרת ל express לקבל ולפענח פרמטרים בפורמט JSON שנשלחים ב Ajax מהדפדפן, באמצעות XMLHttpRequest.

תחילה נתקין את הספריה body parser:

$ npm init -y
$ npm install --save express body-parser

לאחר מכן נכתוב את הקוד הבא בקובץ השרת המעודכן שלנו index.js:

const bodyParser = require('body-parser')
const express = require('express')
const path = require('path');
const app = express()

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

messages = [];

app.get('/m', function (req, res) {
  res.send({messages})
});

app.post('/m', function (req, res) {
  messages.push(req.body);
});

app.delete('/m', function (req, res) {
  messages = [];
});

app.listen(3010, function () {
  console.log('Example app listening on port 3010!')
});

הקוד מתוחכם יותר מהדוגמא הקודמת. במקום להשתמש במונה מספרי לקחתי מערך, ובנוסף לפעולת GET אני מטפל גם בפעולת POST. בכל פעם שגולש שולח הודעת POST אוסיף את ההודעה ששלח למערך, ובכל פעם שנשלחת הודעת GET אשלח חזרה את כל מערך ההודעות.

קבצי ה HTML ו JavaScript של היישום נמצאים בתיקיית public. הפקודה:

app.use(express.static(path.join(__dirname, 'public')));

גורמת ל express לשלוח קבצים סטטיים שנשמרים בתיקיה זו.

צרו את התיקיה public ובתוכה הקבצים שישלחו ללקוח. תחילה index.html עם התוכן הבא:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Index</title>
  </head>
  <body>

    <h1>Messages List</h1>
    <input type="text" />
    <button id="btnSend">Send</button>
    <button id="btnRefresh">Refresh</button>
    <ul>
    </ul>

    <script src="main.js"></script>
  </body>
</html>

ולאחריו main.js עם התוכן הבא:

const ul  = document.querySelector('ul');
const inp = document.querySelector('input');

function insertMessages(event) {
  const xhr = event.target;
  const data = JSON.parse(xhr.responseText);
  for (let msg of data.messages) {
    const li = document.createElement('li');
    li.textContent = `${msg.from} - ${msg.text}`;
    ul.appendChild(li);
  }
}

function refresh() {
  ul.innerHTML = '';
  const xhr = new XMLHttpRequest();
  xhr.open('GET', '/m');
  xhr.addEventListener('load', insertMessages);
  xhr.send();
}

function send() {
  const text = inp.value;
  const from = 'ynon';
  const xhr = new XMLHttpRequest();
  inp.value = '';
  xhr.open('POST', '/m');
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.send(JSON.stringify({from, text}));
}

btnRefresh.addEventListener('click', refresh);
btnSend.addEventListener('click', send);

מוזמנים להריץ אצלכם את השרת או לראות אותו בפעולה במכונה הוירטואלית הבאה:

4. לסיכום

אני אוהב את node.js כי מאוד קל להרים אתו micro services. פשוט כותבים קובץ js עבור ה API, מפעילים ומתחילים לטפל בבקשות. כמובן שלתוכניות אמיתיות יש תמיד נטיה להסתבך, אבל עדיין מהירות הכתיבה והאפשרות לשתף קוד וסגנון עבודה בין צד השרת לצד הלקוח הם בין הגורמים המשכנעים לבחירה בטכנולוגיה זו.

אישית לא הסתדרתי עם פיתוח יישומי Monolith ב node.js, ופריימוורקים מקיפים כגון Rails או Django עדיפים לדעתי לצורך זה (מאחר והם חוסכים את ההתעסקות עם בחירת הספריות הנכונות והתמודדות עם מצבים שמישהו מפסיק לתחזק את הספריה שבדיוק בחרתם). ועדיין עבור Micro Services ו APIs היכולת של node לתת מענה מהיר וגם בביצועים טובים היא משהו שקשה להתחרות בו.