Создание API

Последнее обновление: 23.11.2023

Используя Express и Node.js, мы можем реализовать полноценный API в стиле REST для взаимодействия с пользователем. Архитектура REST предполагает применение следующих методов или типов запросов HTTP для взаимодействия с сервером:

  • GET

  • POST

  • PUT

  • DELETE

Зачастую REST-стиль особенно удобен при создании всякого рода Single Page Application. Рассмотрим, как создать свой API. Для нового проекта создадим новую папку. Сразу добавим в проект пакет express с помощью команды:

npm install express

В данном случае мы создадим экспериментальный проект, который призван продемонстрировать применение REST в приложении на Node.js+Express. Для обработки запросов определим в проекте следующий файл app.js:

const express = require("express");
    
const app = express();
app.use(express.json());
  
app.use(express.static("public"));

// условная база данных
const users = [];
let id = 1;		// для установки идентификаторов

// вспомогательная функция для поиска индекса пользователя по id
function findUserIndexById(id){
	for(let i=0; i < users.length; i++){
        if(users[i].id==id) return i;
    }
	return -1;
}
app.get("/api/users", function(_, res){
       
    res.send(users);
});
// получение одного пользователя по id
app.get("/api/users/:id", function(req, res){
       
    const id = req.params.id; // получаем id
    // находим в массиве пользователя по id
    const index = findUserIndexById(id);
    // отправляем пользователя
    if(index > -1){
        res.send(users[index]);
    }
    else{
        res.status(404).send("User not found");
    }
});
// получение отправленных данных
app.post("/api/users", function (req, res) {
      
    if(!req.body) return res.sendStatus(400);
      
    const userName = req.body.name;
    const userAge = req.body.age;
    const user = {name: userName, age: userAge};
    // присваиваем идентификатор из переменной id и увеличиваем ее на единицу
    user.id = id++;
    // добавляем пользователя в массив
    users.push(user);
    res.send(user);
});
 // удаление пользователя по id
app.delete("/api/users/:id", function(req, res){
       
    const id = req.params.id;
    const index = findUserIndexById(id);
    if(index > -1){
        // удаляем пользователя из массива по индексу
        const user = users.splice(index, 1)[0];
        res.send(user);
    }
    else{
        res.status(404).send("User not found");
    }
});
// изменение пользователя
app.put("/api/users", function(req, res){
       
    if(!req.body) return res.sendStatus(400);
      
    const id = req.body.id;
    const userName = req.body.name;
    const userAge = req.body.age;
      
    const index = findUserIndexById(id);
    if(index > -1){
		// изменяем данные у пользователя
        const user = users[index];
		user.age = userAge;
        user.name = userName;
        res.send(user);
    }
    else{
        res.status(404).send("User not found");
    }
});
   
app.listen(3000, function(){
    console.log("Сервер ожидает подключения...");
});

В данном приложении для простоты в качестве базы данных мы будем использовать обычный массив - массив users. По умолчанию он пуст

const users = [];

Каждый объект, который будет попадать в этот массив, будет иметь определенный числовой идентификатор. И для установки идентификаторов объектов при их создании определим переменную id:

let id = 1;

Также нам потребуется вспомогательная функция для поиска индекса пользователя по id:

function findUserIndexById(id){
	for(let i=0; i < users.length; i++){
        if(users[i].id==id) return i;
    }
	return -1;
}

Если пользователь найден, то возвращается его индекс в массиве. Если не найден, то возвращается число -1.

Для обработки запросов определено пять методов для каждого типа запросов: app.get()/app.post()/app.delete()/app.put()

Когда приложение получает запрос типа GET по адресу "api/users", то срабатывает следующий метод:

app.get("/api/users", function(req, res){
    res.send(users);
});

В качестве результата обработки мы должны отправить массив пользователей методом res.send().

Аналогично работает другой метод app.get(), который срабатывает, когда в адресе указан id пользователя:

app.get("/api/users/:id", function(req, res){
       
    const id = req.params.id; // получаем id
    // находим в массиве пользователя по id
    const index = findUserIndexById(id);
    // отправляем пользователя
    if(index > -1){
        res.send(users[index]);
    }
    else{
        res.status(404).send("User not found");
    }
});

Единственное, что в этом случае нам надо найти нужного пользователя по id в массиве, а если он не был найден, возвратить статусный код 404: res.status(404).send().

При получении запроса методом POST извлекаем отправленные клиентом данные из запроса:

app.post("/api/users", function (req, res) {
      
    if(!req.body) return res.sendStatus(400);
      
    const userName = req.body.name;
    const userAge = req.body.age;
    const user = {name: userName, age: userAge};
    // присваиваем идентификатор из переменной id и увеличиваем ее на единицу
    user.id = id++;
    // добавляем пользователя в массив
    users.push(user);
    res.send(user);
});

Поскльку вы начале файла мы встроили в конвейер обработки запроса автоматический парсинг в json, тело запроса будет представлять объект json, из которого мы можем взять свойства name и age и создать по ним объект пользователя. Для установки идентификатора применяем глобальную переменную id, значение которой инкрементируется.

При удалении производим похожие действия, только теперь извлекаем из массива удаляемый объект:

 // удаление пользователя по id
app.delete("/api/users/:id", function(req, res){
       
    const id = req.params.id;
    const index = findUserIndexById(id);
    if(index > -1){
        // удаляем пользователя из массива по индексу
        const user = users.splice(index, 1)[0];
        res.send(user);
    }
    else{
        res.status(404).send("User not found");
    }
});

Если объект не найден, возвращаем статусный код 404.

Если приложению приходит PUT-запрос, то он обрабатывается методом app.put(), в котором получаем измененные данные:

app.put("/api/users", function(req, res){
       
    if(!req.body) return res.sendStatus(400);
      
    const id = req.body.id;
    const userName = req.body.name;
    const userAge = req.body.age;
      
    const index = findUserIndexById(id);
    if(index > -1){
		// изменяем данные у пользователя
        const user = users[index];
		user.age = userAge;
        user.name = userName;
        res.send(user);
    }
    else{
        res.status(404).send("User not found");
    }
});

Здесь также для поиска изменяемого объекта находим по id его индекс, по индексу получаем объект и изменяем у него свойства.

Таким образом, мы определили простейший API. Теперь добавим код клиента. Итак, как установлено в коде, Express для хранения статических файлов использует папку public, поэтому создадим в проекте подобную папку. В этой папке определим новый файл index.html, который будет выполнять роль клиента. В итоге весь проект будет выглядеть следующим образом:

  • app.js

  • public

    • index.html

Далее определим в файле index.html следующий код:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>METANIT.COM</title>
    <style>
    td, th {padding:5px;min-width:90px;max-width:200px; text-align:start;}
    .btn {padding:4px; border:1px solid #333; background-color: #eee; border-radius: 2px; margin:5px; cursor:pointer;}
    </style>
</head>
<body>
<h2>Список пользователей</h2>
<form name="userForm">
    <input type="hidden" name="id" value="0" />
    <p>
        <label>Имя:</label><br>
        <input name="name" />
    </p>
    <p>
        <label>Возраст:</label><br>
        <input name="age" type="number" />
    </p>
    <p>
        <button id="submitBtn" type="submit">Сохранить</button>
        <button id="resetBtn">Сбросить</button>
    </p>
</form>
<table>
    <thead><tr><th>Id</th><th>Имя</th><th>Возраст</th><th></th></tr></thead>
    <tbody></tbody>
</table>
<script>
const tbody = document.querySelector("tbody");
// Получение всех пользователей
async function GetUsers() {
// отправляет запрос и получаем ответ
    const response = await fetch("/api/users", {
        method: "GET",
        headers: { "Accept": "application/json" }
    });
    // если запрос прошел нормально
    if (response.ok === true) {
    // получаем данные
    const users = await response.json(); 
        users.forEach(user => {
            // добавляем полученные элементы в таблицу
            tbody.append(row(user));
        });
    }
}
// Получение одного пользователя
async function GetUser(id) {
    const response = await fetch("/api/users/" + id, {
        method: "GET",
        headers: { "Accept": "application/json" }
    });
    if (response.ok === true) {
        const user = await response.json();
        const form = document.forms["userForm"];
        form.elements["id"].value = user.id;
        form.elements["name"].value = user.name;
        form.elements["age"].value = user.age;
    }
}
// Добавление пользователя
async function CreateUser(userName, userAge) {
    const response = await fetch("api/users", {
        method: "POST",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({
            name: userName,
            age: parseInt(userAge, 10)
        })
    });
    if (response.ok === true) {
        const user = await response.json();
        reset();
        tbody.append(row(user));
    }
}
// Изменение пользователя
async function EditUser(userId, userName, userAge) {
    const response = await fetch("api/users", {
        method: "PUT",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({
            id: userId,
            name: userName,
            age: parseInt(userAge, 10)
        })
    });
    if (response.ok === true) {
        const user = await response.json();
        reset();
        document.querySelector(`tr[data-rowid="${user.id}"]`).replaceWith(row(user));
    }
}
// Удаление пользователя
async function DeleteUser(id) {
    const response = await fetch("/api/users/" + id, {
        method: "DELETE",
        headers: { "Accept": "application/json" }
    });
    if (response.ok === true) {
        const user = await response.json();
        document.querySelector(`tr[data-rowid="${user.id}"]`).remove();
    }
}
   
// сброс формы
function reset() {
    const form = document.forms["userForm"];
    console.log(form);
    form.reset();
    form.elements["id"].value = 0;
}
// создание строки для таблицы
function row(user) {
   
    const tr = document.createElement("tr");
    tr.setAttribute("data-rowid", user.id);
       
    const idTd = document.createElement("td");
    idTd.append(user.id);
    tr.append(idTd);
       
    const nameTd = document.createElement("td");
    nameTd.append(user.name);
    tr.append(nameTd);
       
    const ageTd = document.createElement("td");
    ageTd.append(user.age);
    tr.append(ageTd);
       
    const linksTd = document.createElement("td");
       
    const editLink = document.createElement("a");
    editLink.setAttribute("data-id", user.id);
    editLink.setAttribute("class", "btn");
    editLink.append("Изменить");
    editLink.addEventListener("click", e => {
        e.preventDefault();
        GetUser(user.id);
    });
    linksTd.append(editLink);
       
    const removeLink = document.createElement("a");
    removeLink.setAttribute("data-id", user.id);
    removeLink.setAttribute("class", "btn");
    removeLink.append("Удалить");
    removeLink.addEventListener("click", e => {
        e.preventDefault();
        DeleteUser(user.id);
    });
       
    linksTd.append(removeLink);
    tr.appendChild(linksTd);
       
    return tr;
}
// сброс значений формы
document.getElementById("resetBtn").addEventListener("click", e => {
    e.preventDefault();
    reset();
});
   
// отправка формы
document.forms["userForm"].addEventListener("submit", e => {
    e.preventDefault();
    const form = document.forms["userForm"];
    const id = form.elements["id"].value;
    const name = form.elements["name"].value;
    const age = form.elements["age"].value;
    if (id == 0)
        CreateUser(name, age);
    else
        EditUser(id, name, age);
});
   
// загрузка пользователей
GetUsers();
</script>
</body>
</html>

Основная логика здесь заключена в коде javascript. При загрузке страницы в браузере получаем все объекты из БД с помощью функции GetUsers:

async function GetUsers() {
    // отправляет запрос и получаем ответ
    const response = await fetch("/api/users", {
        method: "GET",
        headers: { "Accept": "application/json" }
    });
    // если запрос прошел нормально
    if (response.ok === true) {
        // получаем данные
        const users = await response.json(); 
        users.forEach(user => {
            // добавляем полученные элементы в таблицу
            tbody.append(row(user));
        });
    }
}

Для добавления строк в таблицу используется функция row(), которая возвращает строку. В этой строке будут определены ссылки для изменения и удаления пользователя.

Ссылка для изменения пользователя с помощью функции GetUser() получает с сервера выделенного пользователя:

async function GetUser(id) {
    const response = await fetch("/api/users/" + id, {
        method: "GET",
        headers: { "Accept": "application/json" }
    });
    if (response.ok === true) {
        const user = await response.json();
        const form = document.forms["userForm"];
        form.elements["id"].value = user.id;
        form.elements["name"].value = user.name;
        form.elements["age"].value = user.age;
    }
}

И выделенный пользователь добавляется в форму над таблицей. Эта же форма применяется и для добавления объекта. С помощью скрытого поля, которое хранит id пользователя, мы можем узнать, какое действие выполняется - добавление или редактирование. Если id равен 0, то выполняется функция CreateUser, которая отправляет данные в POST-запросе:

async function CreateUser(userName, userAge) {
    const response = await fetch("api/users", {
        method: "POST",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({
            name: userName,
            age: parseInt(userAge, 10)
        })
    });
    if (response.ok === true) {
        const user = await response.json();
        reset();
        tbody.append(row(user));
    }
}

Если же ранее пользователь был загружен на форму, и в скрытом поле сохранился его id, то выполняется функция EditUser, которая отправляет PUT-запрос:

async function EditUser(userId, userName, userAge) {
    const response = await fetch("api/users", {
        method: "PUT",
        headers: { "Accept": "application/json", "Content-Type": "application/json" },
        body: JSON.stringify({
            id: userId,
            name: userName,
            age: parseInt(userAge, 10)
        })
    });
    if (response.ok === true) {
        const user = await response.json();
        reset();
        document.querySelector(`tr[data-rowid="${user.id}"]`).replaceWith(row(user));
    }
}

Запустим приложение, обратимся в браузере по адресу "http://localhost:3000" и мы сможем добавлять новых пользователей:

REST и API в Express и Node.js

Аналогично пользователей можно изменять и удалять.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850