Создаём API при помощи Node.js и Express


Код на гитхабе https://github.com/tutsplus/code-your-first-api-with-nodejs-and-express-connect-a-database

1. Разбираемся с REST API

REST Representational State Transfer (* передача репрезентативного состояния) – абстрактная концепция

REST предполагает возможность передачи части данных по запросу
Например, у нас есть коллекция данных о птицах, но по запросу "зяблик" мы передаём данные только о зяблике, и не передаём данные о воробьях и синицах.

RESTful API – API, который работает согласно принципам REST.
API получает запрос и отправляет ответ - данные в формате JSON

Принципы REST

1. Унифицированный интерфейс - запрос отправляется при помощи URI
2. Клиент-Сервер: сервер и клиент выполняют различные задачи: сервер хранит данные и выполняет с ними манипуляции, а клиент отправляет запросы и отображает ответы.
3. Взаимодействия без запоминания состояния: вся информация о каждом запросе содержится в нем самом и не зависит от состояния сессии.
4. Возможность использования кэш-памяти: клиент и сервер могут кэшировать ресурсы.

HTTP (HyperText Transfer Protocol) – метод взаимодействия клиентов с серверами в Интернете.

HTTP открывает соединение TCP (Transmission Control Protocol – протокол управления передачей) к порту сервера (80 для http, 443 для https).
По этому соединению клиент отправляет запрос, а сервер отправляет ответы, которые содержат код ответа и тело.

Методы запроса

GET - получение данных. Когда открыли страницу, это метод GET
POST - отправка данных на сервер. Например, регистрация пользователя это метод POST
PUT - обновление данных, например, при изменении настроек
DELETE

Коды ответа

1xx: Information (* Информация о процессе передачи)
2xx: Success (* Информация об успешном принятии и обработке запроса клиента)
3xx: Redirection (* Перенаправление)
4xx: Client Error (* Информация об ошибках со стороны клиента)
5xx: Server Error (* Информация об ошибках со стороны сервера)

Примеры

200 OK – запрос был выполнен успешно
404 Not Found - запрашиваемый ресурс не существует
301 Moved Permanently - перенаправление
500 Internal Server Error - ошибка сервера

Что касается API RESTful, коды всех ответов должны находиться в диапазоне 2xx.

ЗапросОтвет
GET200 (OK)
POST201 (Created) (* Создано)
PUT200 (OK)
DELETE200 (OK),
202 (Accepted) (* Принято)
204 (No Content) (* Нет содержимого)
cURL – console URL - инструмент командной строкиКоманда

curl -i https://www.google.com

отправляет запрос по методу GET
Ответ начинается с HTTP/2 200

Команда

curl -i https://google.com

тоже отправляет запрос по методу GET
Но теперь ответ начинается с HTTP/2 301 - вначале происходит перенаправление

Эндпойнт - функция API, которая запускается при обращении к серверу по определенному URL и может принимать и обрабатывать запросы по методу GET, POST, PUT или DELETE.

URL API

Общепринятые рекомендации

1. В URL должны использоваться существительные, а не глаголы
2. Cуществительные должны быть указаны во множественном числе
3. В URL не должно содержаться расширения файла



Создаём папку express-api

Инициализируем проект

npm init -y

Устанавливаем модули

body-parser – для разбора тела запросов;
express – для создания нашего сервера;
mysql: драйвер MySQL;
request – для выполнения запросов HTTP (необязателен) ;

npm install body-parser express mysql request

Создаём сервер на node.js


const http = require('http');
const port = 3001;
const server = http.createServer((requestresponse=> {
  response.end('Hello, server!')
})
server.listen(port, (error=> {
  if (error) return console.log(`Error: ${error}`);
  console.log(`Server is listening on port ${port}`)
})

Запустим в терминале команду
curl -i http://localhost:3001

Ответ сервера

HTTP/1.1 200 OK
Date: Sat, 01 Aug 2020 19:50:02 GMT
Connection: keep-alive
Content-Length: 14
Hello, server!

Создаем сервер Express

Создадим новый файл, app.js

Пишем код

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

app.get('/', (requestresponse=> {
  response.send('Hello, Express!');
});

const server = app.listen(port, (error=> {
  if (error) return console.log(`Error: ${error}`);
  console.log(`Server listening on port ${server.address().port}`);
});

Запустим в терминале команду

curl -i http://localhost:3000

Ответ сервера немного другой. Указано, что сервер работает на базе Express и что есть заголовок Content-Type

HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html; charset=utf-8
Content-Length: 14
ETag: W/"e-gaHDsc0MZK+LfDiTM4ruVL4pUqI"
Date: Sat, 01 Aug 2020 19:56:54 GMT
Connection: keep-alive
Hello, Server!

Устанавливаем модуль body-parser
Этот модуль нужен для удобного разбора тела запроса подробнее
Подключаем его 

const bodyParser = require('body-parser');

Затем мы укажем нашему приложению Express, что необходимо использовать body-parser и преобразовывать данные в формат JSON.

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true,
}));

Ичтобы вместо простого текста в качестве ответа отправлялся JSON-объектзаменим

app.get('/', (requestresponse=> {
  response.send('Hello, Express!');
});

на

app.get('/', (requestresponse=> {
  response.send({message: 'Hello, Express!'});
});

Всё вместе

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

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
    extended: true,
}));

app.get('/', (requestresponse=> {
  response.send({message: 'Hello, Express!'});
});

const server = app.listen(port, (error=> {
  if (error) return console.log(error);
  console.log(`Server listening on port ${server.address().port}`);
});

Запрос

curl -i http://localhost:3000

вернёт

Content-Type: application/json; charset=utf-8

Настраиваем маршруты

Создадим новую папку под названием routes и файл под названием routes.js. Подключим его в app.js.

const routes = require('./routes/routes');

В routes.js пишем код

const router = app => {
  app.get('/', (requestresponse=> {
      response.send({
          message: 'Node.js and Express REST API'
      });
  });
}

module.exports = router;

В app.js вместо app.get() вызываем routes(app)

Создадим переменную users в routes.js с некоторыми выдуманными пользовательскими данными в формате JSON

const users = [
  {
    id: 1,
    name: "Richard Hendricks",
    email: "richard@piedpiper.com",
  },
  {
    id: 2,
    name: "Bertram Gilfoyle",
    email: "gilfoyle@piedpiper.com",
  },
];

Добавим еще один маршрут для обработки запросов по адресу /users и методу GET в наш маршрутизатор и будем отправлять с его помощью пользовательские данные.
  app.get("/users", (requestresponse=> {
    response.send(users);
  });

Теперь можно перейти по ссылке http://localhost:3000/users и увидеть все наши данные

Подключаем базу данных

Скачиваем https://www.mamp.info/en/downloads/ и устанавливаем  MAMP https://www.youtube.com/watch?v=gtZpq-p4OfA

Для просмотра базы данных скачиваем и устанавливаем SQLyog
Создаём новую базу данных
How to create Database and Table using SQLyog https://www.youtube.com/watch?v=y14p5rit35I
Добавляем таблицу с данными
Load MySQL Database using SQLyog https://www.youtube.com/watch?v=zNWEl5POp9A

Создаём папку data и в ней файл config.js.
Код

const mysql = require('mysql');
const config = {
  host: 'localhost',
  user: 'root',
  password: 'root',
  database: 'api',
};
const pool = mysql.createPool(config);
module.exports = pool;

Подключаем config.js. в router.js.
const pool = require("../data/config");

Заменяем

  app.get("/users", (requestresponse=> {
    response.send(users);
  });

на

  app.get("/users", (requestresponse=> {
    pool.query("SELECT * FROM users", (errorresult=> {
      if (error) throw error;
      response.send(result);
    });
  });

По адресу http://localhost:3000/users открывается JSON файл с данными

Получить данные пользователя по его id

В файл router.js добавим ещё один эндпойнт

  app.get("/users/:id", (requestresponse=> {
    const id = request.params.id;

    pool.query("SELECT * FROM users WHERE id = ?", id, (errorresult=> {
      if (error) throw error;

      response.send(result);
    });
  });

По ссылке http://localhost:3000/users/1 открываются данные первого пользователя, по ссылке http://localhost:3000/users/2 - второго

Добавление пользователя

Добавление данных происходит по методу POST
В файл router.js добавим эндпойнт

  app.post("/users", (requestresponse=> {
    pool.query("INSERT INTO users SET ?", request.body, (errorresult=> {
      if (error) throw error;
      response.status(201).send(`User added with ID: ${result.insertId}`);
    });
  });

Пользователя добавим выполнив в терминале команду

curl -d "name=Dinesh Chugtai&email=dinesh@piedpiper.com" http://localhost:3000/users

Изменение данных существующего пользователя.

Для изменения данных существующего пользователя выполняем запрос по методу PUT

Запрос

  app.put('/users/:id', (requestresponse=> {
    const id = request.params.id;
    pool.query('UPDATE users SET ? WHERE id = ?', [request.body, id], (errorresult=> {
        if (error) throw error;
        response.send('User updated successfully.');
    });

Данные пользователя изменим выполнив в терминале команду

curl -X PUT -d "name=Bertram Gilfoyle" -d "email=bertram@piedpiper.com" http://localhost:3000/users/2

Удаление пользователя

    app.delete('/users/:id', (requestresponse=> {
        const id = request.params.id;

        pool.query('DELETE FROM users WHERE id = ?', id, (errorresult=> {
            if (error) throw error;
            response.send('User deleted.');
        });
    });

curl -X DELETE http://localhost:3000/users/4

Отправка запросов при помощи модуля request

Запросы при помощи curl мы использовали только в тестовых целях
Создадим файл post.js
Код

const request = require('request'); 
const json = {
    "name""Dinesh Chugtai",
    "email""dinesh@piedpiper.com",
}; 
request.post({
    url: 'http://localhost:3000/users',
    body: json,
    json: true,
}, function (errorresponsebody) {
    console.log(body);
});

Этот код добавляет ещё одного пользователя, если в терминале выполнить команду node post

Непонятно, правда, как добавить сразу много пользователей

Отправление запросов при помощи веб-формы

Обычно запросы отправляются при помощи HTML-форм
Создадим файл index.html

<body>
    <form action="http://localhost:3000/users" method="post">
        <label for="name">Name</label>
        <input type="text" name="name">
        <label for="email">Email</label>
        <input type="email" name="email">
        <input type="submit">
    </form>
</body>

Открываем в браузере, заполняем поля, отправляем форму.
Данные добавились.