Home

Server

Зміст

  1. http - з коробки
  2. express - конспект
  3. middleware - Проміжне ПЗ
  4. Способи передачі даних на сервер
  5. Роутінг у додатку. Методи
  6. ??? Приклад програм
  7. Змінні оточення
  8. Логування
  9. Що таке REST
  10. Основні методи HTTP
  11. CORS
  12. Формування URL для REST API
  13. Приклад REST API програми
  14. express - практика

http - з коробки. Практика

Матеріал взято з відео уроку Less-3/1

Хост - http://localhost:8080, де localhost - hostname 8080 - port

Port бажано починати більше ніж 1500

Приклад перевіреного коду:

    console.log("Сервер з коробки http-serv.js");
    const http = require("node:http");

    const server = http.createServer((request, response) => {
        const {url, method} = request;

        if(method === "GET" && url === "/") {
            return response.end("Hello http!");
        }

        if(method === "GET" && url === "/movies") {
            return response.end("Hello movies!");
        }
        response.statusCode = 404;
        response.end("Not found");
    });


    // http://localhost:8080
    server.listen(8080, ()=> {
        console.log("Server runing 8080 port");
    });
                    

express - конспект

Express – це мінімалістичний та гнучкий вебфреймворк для застосунків Node.js Давайте відразу приступимо до практики

  1. Create project: $ mkdir myapp; $ cd myapp
  2. $ npm init -y
  3. $ touch .gitignore
  4. $ npm install express
  5. створіть файл з ім'ям app.js та додайте наступний код:
    const express = require('express');
    const app = express();
                    
    app.get('/', (req, res) => {
     res.send('Hello World!');
    });
                    
    app.listen(3000, () => {
     console.log('Example app listening on port 3000!');
    });
                

$ node app.js

Заходимо у браузер, вводимо адрему http://localhost:3000 Застосунок видає відповідь “Hello World!” у браузері на запити, адресовані кореневому URL (/) або маршруту. Для всіх інших шляхів відповіддю буде статус 404 - Not Found.

Маршрутизація визначає, як ваш застосунок відповідає на клієнтський запит до конкретної адреси – URL. Кожен маршрут може мати одну або більше функцій обробки, що виконуються при зіставленні маршруту. Визначення маршруту, відповідно до документації, має наступну структуру:

app.METHOD(PATH, HANDLER)
  • app — це екземпляр express застосунку
  • METHOD — метод запиту HTTP (GET, POST, PUT, PATCH, DELETE).
  • PATH — шлях на сервері, у нашому випадку – це корінь сайту '/'.
  • HANDLER — функція, що виконується при зіставленні маршруту.
  • req - об'єкт запиту
  • res - об'єкт відповіді

Додамо обробник маршруту contact

    app.get('/contact', (req, res) => {
        res.send('Contact page');
    });
                

І тепер за шляхом http://localhost:3000/contact сервер нам буде повертати сторінку із заголовком Contact page.

Але визначення маршрутів, крім простих рядків, можуть також містити регулярні вирази або спеціальні символи підстановок. Зокрема, ми можемо використовувати такі символи, як ?, +, * и ().

  • '/con?tact' - Символ ? у маршруті вказує, що попередній символ може зустрічатися 1 раз або бути відсутнім
  • Символ + вказує, що попередній символ може зустрічатися 1 і більше разів. Цей шлях маршруту зіставляє contact, conntact, connntact тощо.
  • Символ зірочка * вказує, що на місці цього символу може бути будь-яка кількість символів. Цей шлях маршруту зіставляє contact, conxtact, con123tact тощо.

middleware - Проміжне ПЗ

проміжне ПЗ – просто функція, що приймає три аргументи: об'єкт запиту (req), об'єкт відповіді (res) і функцію next. Вбудуємо власне проміжне ПЗ у наш файл app.js

   app.use((req, res, next) => {
       console.log('Наше проміжне ПЗ');
       next();
   });
                

Ця функція нічого не виконує, просто пропускає потік через себе, але в консоль завжди буде вискакувати наше повідомлення.

Функції проміжної обробки (middleware) – це функції, що мають доступ до об'єкту запиту (req), об'єкту відповіді (res) і до наступної функції проміжної обробки в циклі "запит-відповідь" застосунку. Наступна функція проміжної обробки, як правило, позначається змінною next. Функції проміжної обробки виконують наступні завдання:

  • виконують певний код.
  • вносять зміни до об'єктів запитів та відповідей.
  • можуть завершити цикл "запит-відповідь" та перервати обробку запиту.
  • викликають наступну функцію проміжної обробки зі стеку, виконанням функції next().

Передача даних на сервер

Передача параметра в URL

Маршрути можуть містити параметри — іменовані сегменти URL- адреси. Назва параметра повинна містити символи з діапазону [A-Za-z0-9_]. У визначенні маршруту перед параметром ставиться знак двокрапки.

    app.get('/contact/:id', (req, res) => {
        const id = req.params.id;
        res.send(`<h1>Contact</h1> Параметр: ${req.params.id}`);
    });
                

Якщо ми тепер звернемося за маршрутом /contact/123, то req.params.id міститиме значення 123.

Використання параметрів GET запиту

Другий спосіб – це розбір рядка GET запиту. Після URL-адреси, за якою відбувається звернення на сервер, ставиться знак питання ?, за яким слідує список аргументів, розділених символами &. Щось на кшталт

http://localhost:3000/contacts?skip=0&limit=10

Без цього способу не обходиться жодна реалізація пагінації на сторінці. Результат такого запиту знаходиться в об'єкті req.query.

    {
        skip: 0,
        limit: 10
    }
                

Яущо у GET запиті HTTP параметри рядка запиту не задані, наприклад /search, а знака питання і після нього нічого немає, то req.query за замовчуванням містить порожній об'єкт: {}

Фреймворк Express, містить вбудований засіб розбору рядків запитів, тому що це завдання дуже часто зустрічається у веб-розробці.

Надсилання за допомогою форм

Під час надсилання якихось даних на сервер зазвичай використовуються HTTP методи POST, PUT та PATCH. Це стандартний спосіб під час надсилання форми. Розглянемо, як отримувати такі дані в Express.

Формат HTTP повідомлення складається зі списку заголовків та тіла повідомлення. Запит POST від форми стандартно має заголовок Content-Type: application/x-www-form-urlencoded. Насамперед для отримання відправлених даних необхідно підключити парсер через проміжне ПЗ і він уже міститься у фреймворку. Для створення парсера даних від форм застосовується функція urlencoded().:

app.use(express.urlencoded({ extended: false }));

В цю функцію передається об'єкт, який визначає параметри парсингу. Значення extended: false вказує, що результат парсингу буде представляти набір пар ключ-значення, а кожне значення може бути представлене у вигляді рядка або масиву. Коли цей параметр дорівнює true, парсер використовує іншу бібліотеку для розбору формату рядка.

В цю функцію передається об'єкт, який визначає параметри парсингу. Значення extended: false вказує, що результат парсингу буде представляти набір пар ключ-значення, а кожне значення може бути представлене у вигляді рядка або масиву. Коли цей параметр дорівнює true, парсер використовує іншу бібліотеку для розбору формату рядка.

    <form action="/login" method="POST">
        <label for="email">Email</label>
        <input type="text" name="email" id="email" />
        <label for="password">Пароль</label>
        <input type="password" name="password" id="password" />
        <button type="submit">Увійти</button>
    </form>
                

Браузер відправить на URL <урл нашого застосунку>/login дані форми. Це будуть дві змінні: email та password. За це відповідають атрибути name у відповідних тегів input.

Ці дані ми повинні прийняти на стороні сервера наступним обробником

    app.post('/login', (req, res, next) => {
        const { email, password } = req.body;
        // Виконуємо необхідні операції
    });
                

В результаті сервер повинен отримати дані в об'єкті req.body наступного виду:

    {
        email: 'Значення, що було введено в поле input',
        password: 'Значення, що було введено в поле input'
    }
                

Передача JSON

При створенні вебзастосунків на Node.js часто доводиться працювати з даними в JSON форматі. Це основна форма передачі даних для Web-API, ще є формат XML, але він все більше застаріває через свою багатослівність при формуванні даних і виходить із обігу.

Парсер JSON у застосунку підключається наступним чином.

app.use(express.json());

Передати дані у вигляді JSON можна клієнтським JavaScript за допомогою спеціальної утиліти типу Postman

    app.post('/login', (req, res, next) => {
        const { email, password } = req.body;
        // Виконуємо необхідні операції
    });
                

Цей приклад припускає, що надіслано об'єкт JSON з властивостями email та password. І головне у запиту заголовок Content-Type повинен зберігати application/json, а ви повинні надіслати дійсну розмітку JSON.

Роутінг у додатку

Методи Route

За допомогою класу express.Router можна створювати модульні, монтовані обробники маршрутів. Екземпляр Router є комплексною системою проміжних обробників та маршрутизації; тому його часто називають «мінізастосунком»

    const express = require('express');
    const router = express.Router();
    // визначимо домашній роутер
    router.get('/', (req, res) => {
    res.send('Це головний роутер');
    });
    // визначимо роутер about
    router.get('/about', (req, res) => {
    res.send('About');
    });
    module.exports = router;
                

Потім підключимо модуль my-router.js маршрутизації у застосунок:

    const myRouter= require('./my-router');
        ...
    app.use('/my-router', myRouter);
                

Цей застосунок тепер зможе обробляти запити, адресовані ресурсам /my-router та /my-router/about.

Існує також особливий метод маршрутизації, app.all(), що не є похідним від будь-якого методу HTTP. Цей метод використовується для завантаження функцій проміжної обробки на шляху для всіх методів запитів. Він буває корисний, коли нам потрібно реагувати на будь-яке звернення до сервера.

    app.all('/anything', (req, res, next) => {
        console.log('Anything method.');
        next(); // передаємо управління далі
    });
                

У наведеному нижче прикладі обробник буде запущений для запитів, адресованих /anything, незалежно від того, чи використовується GET, POST, PUT, DELETE або будь-який інший метод запиту HTTP, що підтримується в модулі http.

Методи відповіді

Методи в об'єкті відповіді (res), перелічені в таблиці нижче, можуть передавати відповідь клієнту та завершувати цикл “запит-відповідь”. Якщо жоден із цих методів не буде викликано з обробника маршруту, клієнтський запит зависне.

qew

Ланцюжки методів

Метод app.route() дозволяє створювати обробники маршрутів, що утворюють ланцюжки для конкретного шляху маршруту. Оскільки шлях один і той самий для різних методів HTTP, зручно створювати модульні маршрути, щоб мінімізувати надмірність та кількість друкарських помилок. Нижче наведено приклад об'єднаних у ланцюжок обробників маршрутів, визначених за допомогою функції app.route().

    app
    .route('/blog')
    .get((req, res) => {
    res.send('Get a list of blog');
    })
    .post((req, res) => {
    res.send('Add a record to blog');
    })
    .put((req, res) => {
    res.send('Update blog');
    });
                

Змінні оточення

Код доступний усім, хто його бачить, а, значить, і наші секретні ключі також. Як вирішують цю проблему? Правильне рішення – використовувати змінні середовища. Це локальні змінні, які доступні нашому застосунку. Створення цих змінних виконується за допомогою модуля dotenv.

Цей модуль завантажує змінні середовища з файлу .env, який ви створюєте, наприклад, у кореневому каталозі нашого застосунку. Потім ми підключаємо модуль у нашому застосунку і він додає змінні оточення в об'єкт process.env, і вже звідти, не показуючи значення цих змінних, ми можемо використовувати їх у застосунку. Само собою файл .env ми повинні додати до файлу .gitignore

npm install dotenv Дивись приклад

  1. npm install dotenv
  2. require('dotenv').config(); додамо у файл
  3. Потім створюємо файл .env у кореневому каталозі нашого застосункута додаємо в нього змінні.
  4. SECRET_KEY=123456
  5. NODE_ENV=development

Тепер у файлі застосунку app.js будуть доступні будь-які змінні, які ми додали до файлу .env. Ці змінні доступні тепер у застосунку наступним чином

    process.env.SECRET_KEY;
    rocess.env.NODE_ENV;
                

Надалі ми завжди використовуватимемо змінні оточення для доступу до секретних даних, таких як секретні слова для cookie або jwt, url підключення до бази даних та інше.

Логування

В будь-якому застосунку необхідне протоколювання запитів на сервер. Генератор застосунків використовує для цього модуль morgan — гнучкий проміжний компонент для протоколювання запитів з можливістю налаштування формату виведення.

  1. npm i morgan
  2. const morgan = require('morgan');
  3. ...
  4. app.use(logger('dev'))

Для розробки ми використовуємо певний формат журналу dev. Є п'ять наперед визначених форматів, які ми можемо використовувати, щоб легко отримати необхідну інформацію:

  1. combined – використовує режим combined серверу Apache для формату журналів :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length] ":referrer" ":user-agent"
  2. common – використовує режим common серверу Apache для формату журналів :remote-addr - :remote-user [:date[clf]] ":method :url HTTP/:http-version" :status :res[content-length]
  3. dev – формат журналу з колірним кодуванням (за статусом запиту). Маркер буде пофарбований у зелений колір для кодів успіху, червоний – для кодів помилок сервера, жовтий – для кодів помилок клієнта, бірюзовий – для перенаправлення кодів та незабарвлених інформаційних кодів :method :url :status :response-time ms - :res[content-length]
  4. short коротше, ніж формат за замовчуванням :remote-addr :remote-user :method :url HTTP/:http-version :status :res[content-length] - :response-time ms
  5. tiny – найкоротше виведення, лише час відповіді та кілька додаткових елементів. :method :url :status :res[content-length] - :response-time ms
  6. Корисність логера полягає в тому, що він дозволить вам обробити свої журнали аналітичними застосунками, що генерують корисну статистику. При видачі запитів від різних клієнтських застосунків у журналі буде видно рядок, що ідентифікує агента. Можна створювати власні формати журналу. Для цього потрібно передати спеціальний рядок маркерів. Наприклад, наступний формат :method :url :response-time ms буде створювати записи виду

    GET / 15 ms:

    За замовчуванням доступні наступні маркери. Ви також можете визначати нестандартні маркери та перенаправити виведення у файл журналу.

REST

Що таке URI та URL?

Перш ніж ми перейдемо до того, які архітектурні обмеження накладає REST, давайте розберемося з термінологією URL та URI. Терміни URI та URL часто використовуються як синоніми, але це не зовсім одне й те саме.

URI Є ідентифікатором конкретного ресурсу. Як сторінку, книгу або документ.

URL Представляє особливий тип ідентифікатора, який також говорить вам, як отримати до нього доступ

В принципі URI – це ширше поняття і містить URL. Якщо проводити якісь аналогії спрощення, то можна вважати, що URI – це щось на зразок імені, а URL – конкретно ім'я і як до нього дістатися. Наприклад, ISBN книги – це URI, а ось https://goit.ua – це URL тут є ім'я і як до нього дійти – протокол. Тобто, якщо протокол (https, ftp тощо) або присутній, або мається на увазі для домену, ви повинні називати його URL-адресою, навіть якщо це також URI.

Існує наступна традиційна форма запису `URL:

scheme://[login[:password]@]host[:port]][/path][?query][#fragment];
http://user:password@host.com:80/resourse/path/?query=name&ttt=123#hash
                
  • scheme – це мережевий протокол, за яким відбувається звернення до ресурсу
  • login – не обов'язковий параметр ім'я користувача, яке використовується для доступу до ресурсу
  • password – пароль вказаного користувача, якщо він необхідний
  • host – повністю прописане доменне ім'я хоста в системі DNS (goit.ua) або IP-адреса хоста у формі чотирьох груп десяткових чисел, розділених крапками
  • port – порт хоста для підключення, згадайте ми часто використовували http://localhost:3000, де 3000 – це і є порт. Виникає закономірне питання, чому порт не вказується наприклад для URL у браузері. Просто браузер знає, що для протоколу http порт дорівнює 80, а для https він дорівнює 443 і підставляє його за нас
  • path – уточнююча інформація про місцезнаходження ресурсу.
  • query – рядок запиту з переданими на сервер (методом GET) параметрами. Починається із символу ?, роздільник параметрів – знак &. Приклад: ?foo=123&baz=234&bar=value
  • fragment – ідентифікатор із попереднім символом #. Часто його називають хеш – hash або якір. Якорем може бути вказаний заголовок всередині документа або атрибут id елемента. За таким посиланням браузер відкриє сторінку та перемістить вікно перегляду вказаного елемента. Наприклад, посилання на секцію контактів у лендінгу: https://example.com/#contact.

Єдиний інтерфейс

Це обмеження гарантує, що існує спільна мова між серверами та клієнтами, яка дозволяє замінювати або змінювати кожну частину без порушення роботи всієї системи. Це досягається за рахунок 4 додаткових обмежень:

  • ідентифікація ресурсів
  • маніпулювання ресурсами за допомогою уявлень
  • інформативні (самоописові) повідомлення
  • гіпермедіа.

1. Інтернет використовує URL для ідентифікації ресурсів та HTTP в якості стандарту зв'язку. Щоб отримати ресурс, який зберігається на сервері, клієнт відправляє HTTP-запит GET на URL, який ідентифікує цей ресурс. Щоразу, коли ви вводите адресу у свій браузер, ваш браузер робить запит GET на цей URL. Якщо він отримує статус 200 та HTML-документ, він відображає сторінку у вікні, щоб ви могли її переглянути.

2. Найпростіший приклад – це блог у мережі. Коли користувач створює нове повідомлення у блозі, комп'ютер-клієнт повідомляє серверу, що необхідно додати нове повідомлення у блог. Для цього він відправляє HTTP-запит POST, можливо PUT з контентом для нового повідомлення у блозі. Сервер надсилає відповідь клієнту, яка вказує, чи було створено повідомлення або виникла проблема створення запису.

3. Коли користувач вводить http://www.example.com в адресному рядку свого веб-браузера, браузер надсилає наступний HTTP-запит:

    GET / HTTP/1.1
    Host: www.example.com
                    

Кешування

Кешування належить до обмеження, при якому відповіді сервера повинні бути позначені як кешовані або некешовані. Кешування відбувається, коли клієнт зберігає попередні відповіді, отримані від сервера, тому, коли ці дані знову знадобляться, він може не робити запит через мережу, а використовувати кешовані дані. Кешування частково або повністю усуває деякі клієнт-серверні взаємодії, сприяючи масштабованості та продуктивності застосунку.

Основні методи HTTP

Основні HTTP-методи GET/POST/PUT/DELETE

  • Інформаційні 100 - 199
  • Успішні 200 - 299
  • 200 (OK) - вірний запит
  • 201 Create - створено успішно
  • 204 - No Content усе вірно але нема чого передати
  • Перенаправлення 300 - 399
  • 301: Moved Permanently
  • 307: Temporary Redirect
  • Клієнтські помилки 400 - 499
  • 400: Bad Request
  • 401: Unauthorized
  • 403: Forbidden - нема прав доступу
  • 404: Not Found
  • Серверні помилки 500 - 599
  • 500: Internal Server Error
  • 501: Not Implemented
  • 502: Bad Gateway
  • 503: Service Unavailable
  • 504: Gateway Timeout

Детальніше почитайте у стандарті RFC 2616 або простіше MDN

CORS

Cross-Origin Resource Sharing (CORS, перехресний обмін ресурсами) – це механізм, який за допомогою HTTP-заголовків дає браузеру дозвіл завантажувати ресурси з певного джерела на запит вебзастосунку, отриманого з іншого джерела.

З метою безпеки всі браузери припиняють усі перехресні HTTP-запити, які здійснюються клієнтським JavaScript. Це називається дотримання правила одного джерела і виходить, що вебзастосунок, отриманий з певного домену (Github pages), не може виконувати запити до HTTP-ресурсів з домену (Heroku), що відрізняється. Щоб отримати відповідь, що надходить з API, веб-сервер на якому реалізовано API, повинен містити відповідні CORS-заголовки.

Механізм CORS робить безпечні перехресні запити та передачі даних між web-браузерами та web-серверами.

Приклад:

npm install cors

const express = require('express');
const cors = require('cors');
const app = express();
app.use(cors());

app.get('/', (req, res, next) => {
    res.json({ message: 'CORS is activated' });
});
app.listen(3000, function () {
    console.log('CORS-enabled web server listening on port 3000');
});

                

У функцію проміжного ПЗ cors, ми можемо передати аргумент – конфігураційний об'єкт з наступними властивостями

  1. origin: Налаштовує заголовок CORS Access-Control-Allow-Origin. Найчастіше це рядок "*", який дозволяє запит від будь-якого домену. Можливо конкретне значення на кшталт "http://example.com" і будуть дозволені тільки запити з "http://example.com". Можна використовувати регулярний вираз або масив рядків, або регулярних виразів, якщо доступ до API можливий з різних доменів
  2. methods: Налаштовує заголовок CORS Access-Control-Allow-Methods. Очікує рядок з HTTP методами, наприклад, "GET, PUT, POST", можна масив ['GET', 'PUT', 'POST'], яким дозволені міждоменні запити.
  3. allowedHeaders: Налаштовує заголовок CORS Access-Control-Allow-Headers. Чекає рядок з роздільниками-комами, наприклад, "Content-Type, Authorization" або масив ['Content-Type', 'Authorization'] – які заголовки дозволені при запиті.
  4. exposedHeaders: Налаштовує заголовок CORS Access-Control-Expose-Headers. Управляє заголовками користувача.
  5. credentials: Налаштовує заголовок CORS Access-Control-Allow-Credentials. Встановіть true для передачі заголовка, інакше він не вказується.
  6. maxAge: Налаштовує заголовок CORS Access-Control-Max-Age. Встановіть ціле число для передачі заголовка, інакше він не вказується.
  7. preflightContinue: Надіслати відповідь попередньої перевірки CORS наступному обробнику.
  8. optionsSuccessStatus: Надає код стану для використання у разі успішних OPTIONS запитів, оскільки деякі застарілі браузери (IE11, різні SmartTV) не працюють зі статусом 204.
  9. Конфігурація за замовчуванням еквівалентна:
        {
            origin: '*',
            methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
            preflightContinue: false,
            optionsSuccessStatus: 204
        }
                            

Формування URL для REST API

У цьому розділі ми поговоримо про правильне іменування ресурсів для API. Коли ресурси названі правильно, API інтуїтивно зрозумілий та простий у використанні. Часто кінцеві URL за запитом ресурсу називають endpoints API

Що є хорошою практикою для правильного іменування?

  • Ну, по-перше, необхідно використовувати для опису базових URL іменники у множині – users, contacts.
  • Також потрібно використовувати конкретніші та чіткіші імена news, videos, а не абстрактні items або elements
  • Складну логіку для URL необхідно описувати за рахунок додаткових властивостей, тобто ховати за знаком ?
  • Приклад використання пагінації /users?limit=25&offset=50, фільтрація відповіді /friends?fields=id,name,picture

Pозглянемо хороші практики іменування URL для різноманітних ситуацій

  • Додавання нового клієнта до системи:
        HTTP метод: POST
        URL: http://www.example.com/customers
                            
  • Отримати дані клієнта з ідентифікатором клієнта ID 112233:
        HTTP метод: GET
        URL: http://www.example.com/customers/112233
                            
  • Створення нового продукту:
        HTTP метод: POST
        URL: http://www.example.com/products
                            
  • Для читання, оновлення, видалення продукту з ID 432111, відповідно:
        HTTP метод: GET, PUT, DELETE
        URL: http://www.example.com/products/432111
                            
  • Створення нового замовлення для клієнта поза контекстом клієнта
        HTTP метод: POST
        URL: http://www.example.com/orders
                            
  • Створення того самого замовлення, але в контексті конкретного клієнта з ID 332244
        HTTP метод: POST
        URL: http://www.example.com/customers/332244/orders
                            
  • Список замовлень, що належать клієнту ID 332244:
        HTTP метод: GET
        URL: http://www.example.com/customers/332244/orders
                            
  • Нехай необхідний URL для додавання нової позиції на замовлення з ID 1234, для клієнта з ID 332244:
        HTTP метод: POST
        URL: http://www.example.com/customers/332244/orders/1234/lineorders
                            
  • Отримання списку замовлення за ID замовлення без знання ID конкретного клиента
        HTTP метод: GET
        URL: http://www.example.com/orders/8769
                            
  • Пагінація здійснюється через query рядок за допомогою параметра offset – це початковий номер позиції, та параметра limit – максимальна кількість елементів, що повертаються. Вони можуть називатися й інакше, наприклад skip, limit
        HTTP метод: GET
        URL: http://api.example.com/resources?offset=0&limit=25
                            
  • Складна фільтрація за значеннями. Можна використовувати роздільник подвійна двокрапка ::, що відокремлює ім'я властивості від значення порівняння
        HTTP метод: GET
        URL: http://www.example.com/users?filter="name::sam|city::denver"
                            
  • Сортування. Один із способів, коли для кожної переданої властивості здійснюється сортування в порядку зростання, а для кожної властивості, з префіксним тире ("-") сортування здійснюється у порядку зменшення. Сепаратор для кожного імені властивості вертикальна смуга ("|")
        HTTP метод: GET
        URL: http://www.example.com/users?sort=lastName|firstName|-birthdate
                            

Приклад REST API програми

Розглянемо найпростіше Web-API. Реалізуємо стандартний todo-list, який буде нам повертати список поточних завдань. Наш API міститиме повний набір CRUD (Create, Read, Update, Delete) операцій для наших завдань.

Ми будемо працювати з двома URL

  • /api/tasks/ - повний CRUD
  • /api/tasks/:id/status - для зміни статусу завдання, виконано воно чи ні
  • Повний код застосунку: glitch.com/~nodebook-api
  • Всі запити до нашого API ми будемо виконувати за допомогою утиліти

Основний файл застосунку

    const express = require('express');
    const cors = require('cors');
    const routerApi = require('./api');
    const app = express();
    app.use(express.json()); // parse application/json
    app.use(cors()); // cors
    app.use('/api', routerApi);

    app.use((_, res, __) => {
        res.status(404).json({
            status: 'error',
            code: 404,
            message: 'Use api on routes: /api/tasks',
            data: 'Not found',
        });
    });
    app.use((err, _, res, __) => {
        console.log(err.stack);
        res.status(500).json({
            status: 'fail',
            code: 500,
            message: err.message,
            data: 'Internal Server Error',
        });
    });
    const PORT = process.env.PORT || 3000;

    app.listen(PORT, function () {
        console.log(Server running. Use our API on port: ${PORT});
    });
                

Ми тут підключаємо обробку даних у форматі JSON, включаємо cors і додаємо обробники помилок

Реалізація API

У файлі роутингу ми описуємо всю логіку поведінки нашого API.

Усі дані про завдання ми зберігаємо у масиві tasks, де жорстко прописуємо одне завдання.

    let tasks = [
        {
            id: nanoid(),
            title: 'Work',
            text: 'Do it!',
            done: false,
        },
    ];
                

Надалі в цей масив ми будемо додавати нові завдання, видаляти та оновлювати їх

Читання

отримання списку всіх завдань

    router.get('/tasks', (req, res, next) => {
        res.json({
            status: 'success',
            code: 200,
            data: {
            tasks,
            },
        });
    });
                

Тут все досить просто ми віддаємо об'єкт зі статусом 'success', кодом 200 та властивістю data, це наше корисне навантаження, куди ми покладемо масив наших завдань.

Обробник для отримання завдання за ID

    router.get('/tasks/:id', (req, res, next) => {
        const { id } = req.params;
        const [task] = tasks.filter(el => el.id === id);
        res.json({
            status: 'success',
            code: 200,
            data: { task },
        });
    });
                

Створення нового завдання

router.post('/tasks', (req, res, next) => {
    const { title, text } = req.body;
    const task = {
        id: nanoid(),
        title,
        text,
        done: false,
    };
    tasks.push(task);
    res.status(201).json({
        status: 'success',
        code: 201,
        data: { task },
    });
});

//body:
{
    "title": "Vacation",
    "text": "Enjoy"
}
                

Оновлення завдання

    router.put('/tasks/:id', (req, res, next) => {
        const { id } = req.params;
        const { title, text } = req.body;
        const [task] = tasks.filter(el => el.id === id);
        task.title = title;
        task.text = text;
        res.json({
            status: 'success',
            code: 200,
            data: { task },
        });
    });
                

Часткове оновлення

Для статусу ми використовуємо окремий URL та HTTP метод PATCH

    router.patch('/tasks/:id/status', (req, res, next) => {
        const { id } = req.params;
        const { done } = req.body;
        const [task] = tasks.filter(el => el.id === id);
        task.done = done;
        res.json({
            status: 'success',
            code: 200,
            data: { task },
        });
    });
                

Тут для завдання ми змінюємо значення лише його властивості done

    {
        "done": true
    }
                

express. Практика

Матеріал взято з відео уроку Less-3/1

Документація: expressjs.com

$ npm install express --save

Приклад

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

    app.get('/', (req, res) => {
        res.send('Hello World!')
    })

    app.listen(port, () => {
        console.log(`Example app listening on port ${port}`)
    })
                


Методи request:

query параметри:

  • http://localhost:3000/?apikey=123 - задавати квері парамнтри
  • const apiKey = req.query["apikey"]; - отримати квері парамнтри
  • const apiKey = req.query.apikey; - отримати квері парамнтри

Отримати динамічне значення:

  • http://localhost:3000/contacts/id789 - задавати динамічне значення
  • const { id } = req.params; - зчитати динамічне значення

Отримати body:

  • Задається методом POST тіло ключ-значення
  • метод req.body але треба його розпарсити
  •     const jsonParser = express.json(); // на початку. Тоді
    
        app.post("/bols", jsonParser, (req, res, next)=> {
            const bol = req.body;
            console.log(bol); // Треба розпарсити
            res.send("Це post /bols");
        });
                            

методи response (res):

  • res.end(); // Завершує запит
  • res.send("Це post /bols"); // Відправка на фронтенд Тут можна відправити розмітку html
  • res.status(401).send("No autoriza");// Відправка помилки
  • res.status(404).sendFile(filePath);// Відправка HTML файлу
  • res.status(404).send({message: `Route ${req.url} not found!`}) відправляє JSON
  • res.send(books); books = json. ok
  • res.json(books); books = json ok

Міделвари

  • корінь сайту: http://localhost:8080

        app.get("/", (req, res) => {
            res.send("Це корінь сайту");
        });
  •     app.get("/books", (req, res)=> {
            res.json(books);
        })
                            
  • Додавання обробки помилки якщо не співпав жоден роут. За змовчува-м express шле базову HTML з помилкою. Спрацьовує на http://localhost:8080/yupi

        app.use("/yupi", (req, res, next)=> {
            res.status(404).send({message: `Route yupi ${req.url} not found!`});
        });
  • Спрацьовує на усе інше (помилка) та відправляє JSON

        app.use((req, res, next)=> {
            res.status(404).send({message: `Route ${req.url} not found!`})
        });
  • Спрацьовує та відправляє файл HTML

        app.use((req, res, next)=> {
            const filePath = path.join(__dirname, "pages", "page-404.html");
            res.status(404).sendFile(filePath);
        });
  • Обробка неочікуваної помилки

        app.use((error, __, res, next)=> {
            console.error(error);
            res.status(500).send({ message: "Some Trouble" });
        });
                            
  • middleware для запису логів

        const moment = require("moment");
    
        app.use(async(req, res, next) => {
            const {method, url} = req;
            const date = moment().format("DD-MM-YYYY_hh:mm:ss");
            await fs.appendFile("./public/server.log", `\n${method} ${url} ${date}`);
            next();
        })

Логування

morgan

nom i morgan

дивись документацію щоб записувати у файл

    const morgan = require("morgan"); // Логування nodejs
    app.use(morgan('combined')); // Логування
                

moment

nom i moment

const moment = require("moment");
    // === middleware для запису логів===
    app.use(async(req, res, next) => {
        const {method, url} = req;
        const date = moment().format("DD-MM-YYYY_hh:mm:ss");
        await fs.appendFile("./public/server.log", `\n${method} ${url} ${date}`);
        next();
    }) 


CORS

прибирає конфлікт між різними серверами і браузером

  • npm i cors
  • const cors = require("cors");
  • app.use(cors()); - для усіх адресів
  • або для конкретних адресів
  •     app.use(cors({
            origin: "http://127.0.0.1:5500",
            optionsSuccessStatus: 200,
        })); 
                            

Опис цікавого:

  • Нормально працює тільки через постман
  • app.use((req, res, next)=> {}) - міделвара для усіх запитів
  • app.use("/books", (req, res, next)=> {}) - міделвара для "/books"