Express и MongoDB

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

Теперь объединим в одном приложении обработку запросов с помощью Express и работу с данными в MongoDB. Для этого определим следующий файл приложения app.js:

const express = require("express");
const MongoClient = require("mongodb").MongoClient;
const objectId = require("mongodb").ObjectId;
     
const app = express();
app.use(express.static("public"));  // статические файлы будут в папке public
app.use(express.json());        // подключаем автоматический парсинг json
   
const mongoClient = new MongoClient("mongodb://127.0.0.1:27017/");
  
(async () => {
     try {
        await mongoClient.connect();
        app.locals.collection = mongoClient.db("usersdb").collection("users");
        app.listen(3000);
        console.log("Сервер ожидает подключения...");
    }catch(err) {
        return console.log(err);
    } 
})();
  
app.get("/api/users", async(req, res) => {
          
    const collection = req.app.locals.collection;
    try{
        const users = await collection.find({}).toArray();
        res.send(users);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }  
});
app.get("/api/users/:id", async(req, res) => {
          
    const collection = req.app.locals.collection;
    try{
        const id = new objectId(req.params.id);
        const user = await collection.findOne({_id: id});
        if(user) res.send(user);
        else res.sendStatus(404);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});
     
app.post("/api/users", async(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};
         
    const collection = req.app.locals.collection;
      
    try{
        await collection.insertOne(user);
        res.send(user);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});
      
app.delete("/api/users/:id", async(req, res)=>{
          
    const collection = req.app.locals.collection;
    try{
        const id = new objectId(req.params.id);
        const user = await collection.findOneAndDelete({_id: id});
        if(user) res.send(user);
        else res.sendStatus(404);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});
     
app.put("/api/users", async(req, res)=>{
          
    if(!req.body) return res.sendStatus(400);
    const userName = req.body.name;
    const userAge = req.body.age;
         
    const collection = req.app.locals.collection;
    try{
        const id = new objectId(req.body.id);
        const user = await collection.findOneAndUpdate({_id: id}, { $set: {age: userAge, name: userName}},
         {returnDocument: "after" });
        if(user) res.send(user);
        else res.sendStatus(404);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});
   
// прослушиваем прерывание работы программы (ctrl-c)
process.on("SIGINT", async() => {
      
    await mongoClient.close();
    console.log("Приложение завершило работу");
    process.exit();
});

Для каждого типа запросов здесь определен свой обработчик Express. И в каждом из обработчиков мы каждый раз обращаемся к базе данных. Чтобы не открывать и закрывать подключение каждый раз при каждом запросе, мы открываем подключение в самом начале в IIFE-функции и только после открытия подключения запускаем прослушивание входящих запросов:

(async () => {
     try {
        await mongoClient.connect();
        app.locals.collection = mongoClient.db("usersdb").collection("users");
        app.listen(3000);
        console.log("Сервер ожидает подключения...");
    }catch(err) {
        return console.log(err);
    } 
})();

Поскольку все взаимодействие будет идти с коллекцией users, то получаем ссылку на эту коллекцию в локальную переменную приложения app.locals.collection. Затем через эту переменную мы сможем получить доступ к коллекции в любом месте приложения.

В конце работы скрипта мы можем закрыть подключение, сохраненное в переменную dbClient:

process.on("SIGINT", async() => {
     
    await mongoClient.close();
    console.log("Приложение завершило работу");
    process.exit();
});

В данном случае мы прослушиваем событие "SIGINT", которое генерируется при нажатии комбинации CTRL+C в консоли, что завершит выполнение скрипта.

Когда приходит GET-запрос к приложению, то возвращаем в ответ клиенту все документы из базы данных:

app.get("/api/users", async(req, res) => {
         
    const collection = req.app.locals.collection;
    try{
        const users = await collection.find({}).toArray();
        res.send(users);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }  
});

Если в GET-запросе передается параметр id, то возвращаем только одного пользователя из базы данных по этому id:

app.get("/api/users/:id", async(req, res) => {
         
    const collection = req.app.locals.collection;
    try{
        const id = new objectId(req.params.id);
        const user = await collection.findOne({_id: id});
        if(user) res.send(user);
        else res.sendStatus(404);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});

Когда приходит POST-запрос, с помощью парсера jsonParser получаем отправленные данные и по ним создаем объект, который добавляем в базу данных посредством метода insertOne():

app.post("/api/users", jsonParser, async(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};
        
    const collection = req.app.locals.collection;
     
    try{
        await collection.insertOne(user);
        res.send(user);
    }
    catch(err){
        console.log(err);
        res.sendStatus(500);
    }
});

При получении PUT-запроса также получаем отправленные данные и с помощью метода findOneAndUpdate() обновляем данные в БД.

И в методе app.delete(), который срабатывает при получении запроса DELETE, вызываем метод findOneAndDelete() для удаления данных.

Таким образом, в каждом обработчике Express задействуем определенный метод по работе с MongoDB.

Теперь создадим в папке проекта новый каталог "public" и определим в этом каталоге файл index.html:

Express и MongoDB в приложении на Node.js

В файле 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>

В принципе код index.html вкратце обсуждался в статье про создание API в Node.js, здесь же весь код практически повторяется.

И поскольку Express в качестве хранилища статических файлов использует папку public, то при обращении к приложению по корневому маршруту http://localhost:3000 клиент получит данный файл.

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

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