Разработка веб-приложения для хранения заметок с использованием Express.js.





Разработка веб-приложения для хранения заметок с использованием Express.js

Современные веб-приложения играют важную роль в организации и управлении личной информацией. Одной из популярных категорий таких приложений являются нотатники — сервисы для создания, хранения и редактирования заметок. С помощью заметок пользователи могут быстро фиксировать идеи, списки задач, важные мысли или любые другие данные, нуждающиеся в сохранении и быстром доступе. В этой статье мы подробно рассмотрим процесс разработки простого, но функционального веб-приложения для хранения заметок, используя популярный веб-фреймворк Express.js.

Express.js — это минималистичный и гибкий веб-фреймворк для Node.js, который позволяет быстро разрабатывать серверные приложения и RESTful API. Благодаря простоте настройки и большому сообществу, Express.js становится отличным выбором для реализации как небольших проектов, так и масштабируемых решений. Мы рассмотрим архитектуру приложения, работу с данными, маршрутизацию, обработку запросов и ответы, а также основные аспекты безопасности и организацию пользовательского интерфейса.

Основные требования и архитектура приложения

Перед началом программирования важно определить ключевые функциональные требования к нашему приложению для заметок. Такой подход позволяет структурировать весь процесс разработки и избежать лишних сложностей на ранних этапах.

Наше приложение должно обеспечить следующие возможности:

  • Создание новых заметок с заголовком и текстом;
  • Просмотр списка всех заметок с возможностью выбора конкретной;
  • Редактирование и удаление уже существующих заметок;
  • Простая и интуитивно понятная навигация;
  • Обработка ошибок и валидация данных.

Архитектурно приложение будет представлять собой классический клиент-сервер: сервер на Express.js будет обрабатывать HTTP-запросы, взаимодействовать с базой данных или файловой системой для хранения заметок, а клиентский интерфейс будет отправлять запросы и отображать полученную информацию.

Выбор способа хранения данных

Существует несколько вариантов хранения заметок: использование реляционных или NoSQL баз данных, файловая система либо в некоторых случаях — хранение только на клиенте. Для простоты и быстрого старта мы рассмотрим два возможных варианта:

  • JSON-файл — удобен для небольших приложений и демонстрационных проектов. Все заметки хранятся в одном или нескольких файлах на сервере;
  • MongoDB — более масштабируемый и привычный вариант, основанный на NoSQL базе данных, хорошо интегрирующийся с Node.js.

В рамках этой статьи мы остановимся на хранении заметок в JSON-файле, что позволит сосредоточиться на изучении работы Express.js без необходимости настройки БД. При этом методы взаимодействия с данными и маршрутизация будут универсальными и легко адаптируемыми под полноценную базу в будущем.

Настройка проекта и установка зависимостей

Перед началом разработки создадим новую папку для проекта и инициализируем в ней Node.js проект. Для этого используется команда, которая создаст файл конфигурации package.json с основными метаданными.

Далее установим необходимые пакеты. Основным является Express.js, который будет отвечать за обработку HTTP-запросов и маршрутизацию. Для удобства работы с формами и JSON запросами понадобится middleware для разбора тела запросов.

Шаги настройки проекта

  1. Создайте каталог проекта: mkdir notes-app и перейдите в него.
  2. Инициализируйте проект командой: npm init -y.
  3. Установите Express.js: npm install express.
  4. Установите дополнительный пакет для автоматической перезагрузки сервера во время разработки — nodemon (опционально): npm install -D nodemon.
  5. Создайте файл index.js, который станет точкой входа для сервера.

Ваш package.json файл после установки должен содержать примерно такую структуру с зависимостями:

Поле Значение
name notes-app
version 1.0.0
dependencies
  • express
devDependencies
  • nodemon

Создание базового сервера на Express.js

После установки всех необходимых модулей приступим к реализации базового сервера на Express.js. Он будет слушать запросы по определенному порту и обрабатывать маршруты.

Для начала опишем в файле index.js минимальный серверный код:

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

// Middleware для парсинга JSON тела запросов
app.use(express.json());

// Пример простого маршрута
app.get('/', (req, res) => {
    res.send('Добро пожаловать в приложение для хранения заметок!');
});

app.listen(PORT, () => {
    console.log(`Сервер запущен на порту ${PORT}`);
});

Запустите сервер командой node index.js или npx nodemon index.js для автоматической перезагрузки при сохранении изменений. Откройте в браузере http://localhost:3000 и убедитесь, что выводится приветственное сообщение.

Обработка статических файлов

Для отображения клиентского интерфейса (HTML, CSS, JavaScript) необходимо подключить статические файлы. Express.js упрощает эту задачу с помощью встроенного middleware express.static.

Создайте папку public, куда будут помещены файлы интерфейса. Подключите ее к серверу следующим образом:

app.use(express.static('public'));

Теперь при запросах к серверу будет доступен весь фронтенд-контент. Это позволит сосредоточиться на разработке API и клиентских страниц отдельно.

Реализация CRUD операций для заметок

Ключевой функционал приложения — создание (Create), чтение (Read), обновление (Update) и удаление (Delete) заметок. Для этого настроим соответствующие маршруты с методами HTTP.

Перед реализацией маршрутов организуем работу с данными и определим модуль для чтения и записи заметок в JSON-файл.

Организация хранения заметок

Создадим в корне проекта файл notes.json, который будет содержать массив заметок в формате JSON. Каждая заметка представляет собой объект с полями id, title, content, а также отметкой времени.

Пример содержимого файла:

[
  {
    "id": 1,
    "title": "Первая заметка",
    "content": "Это тестовая заметка.",
    "createdAt": "2025-05-16T07:00:00.000Z"
  }
]

Для удобства создадим отдельный модуль notesStore.js, который инкапсулирует операции чтения и записи заметок.

const fs = require('fs');
const path = require('path');

const NOTES_FILE = path.join(__dirname, 'notes.json');

function getAllNotes() {
    if (!fs.existsSync(NOTES_FILE)) {
        fs.writeFileSync(NOTES_FILE, JSON.stringify([]));
    }
    const data = fs.readFileSync(NOTES_FILE);
    return JSON.parse(data);
}

function saveNotes(notes) {
    fs.writeFileSync(NOTES_FILE, JSON.stringify(notes, null, 2));
}

function addNote(note) {
    const notes = getAllNotes();
    notes.push(note);
    saveNotes(notes);
}

function updateNote(updatedNote) {
    const notes = getAllNotes();
    const index = notes.findIndex(n => n.id === updatedNote.id);
    if (index !== -1) {
        notes[index] = updatedNote;
        saveNotes(notes);
        return true;
    }
    return false;
}

function deleteNote(id) {
    let notes = getAllNotes();
    const lengthBefore = notes.length;
    notes = notes.filter(n => n.id !== id);
    if (notes.length < lengthBefore) {
        saveNotes(notes);
        return true;
    }
    return false;
}

module.exports = {
    getAllNotes,
    addNote,
    updateNote,
    deleteNote
};

Настройка маршрутов API

Подключите модуль хранения заметок в index.js и создайте маршруты по REST-стандартам:

  • GET /api/notes — получить все заметки;
  • GET /api/notes/:id — получить заметку по id;
  • POST /api/notes — создать новую заметку;
  • PUT /api/notes/:id — обновить существующую заметку;
  • DELETE /api/notes/:id — удалить заметку.
const express = require('express');
const app = express();
const { getAllNotes, addNote, updateNote, deleteNote } = require('./notesStore');
const PORT = 3000;

app.use(express.json());

// Получить все заметки
app.get('/api/notes', (req, res) => {
    const notes = getAllNotes();
    res.json(notes);
});

// Получить заметку по id
app.get('/api/notes/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const notes = getAllNotes();
    const note = notes.find(n => n.id === id);
    if (note) {
        res.json(note);
    } else {
        res.status(404).json({ error: "Заметка не найдена" });
    }
});

// Создать новую заметку
app.post('/api/notes', (req, res) => {
    const { title, content } = req.body;
    if (!title || !content) {
        return res.status(400).json({ error: "Необходимо указать заголовок и содержимое" });
    }
    const notes = getAllNotes();
    const newId = notes.length ? notes[notes.length - 1].id + 1 : 1;
    const newNote = {
        id: newId,
        title,
        content,
        createdAt: new Date().toISOString()
    };
    addNote(newNote);
    res.status(201).json(newNote);
});

// Обновить заметку
app.put('/api/notes/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const { title, content } = req.body;
    if (!title || !content) {
        return res.status(400).json({ error: "Необходимо указать заголовок и содержимое" });
    }
    const updatedNote = {
        id,
        title,
        content,
        createdAt: new Date().toISOString()
    };
    const success = updateNote(updatedNote);
    if (success) {
        res.json(updatedNote);
    } else {
        res.status(404).json({ error: "Заметка не найдена" });
    }
});

// Удалить заметку
app.delete('/api/notes/:id', (req, res) => {
    const id = parseInt(req.params.id);
    const success = deleteNote(id);
    if (success) {
        res.json({ message: "Заметка удалена" });
    } else {
        res.status(404).json({ error: "Заметка не найдена" });
    }
});

app.listen(PORT, () => {
    console.log(`Сервер запущен на порту ${PORT}`);
});

Данные маршруты обеспечат работу основного API и позволят интегрировать клиентскую часть с сервером.

Создание клиентского интерфейса

Для взаимодействия пользователя с приложением разработаем простой фронтенд на HTML, CSS и JavaScript. Интерфейс должен позволять просматривать список заметок, создавать новые, редактировать и удалять существующие.

Файлы клиентской части разместим в папке public. Опишем базовую структуру HTML и добавим стили и скрипт для управления логикой.

Структура HTML и стилизация

Пример файла public/index.html с базовой разметкой и подключением внешнего CSS и JS:

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Приложение для заметок</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 20px;
            max-width: 800px;
        }
        h1 {
            text-align: center;
            color: #333;
        }
        #notesList {
            list-style-type: none;
            padding: 0;
        }
        #notesList li {
            background: #f9f9f9;
            border: 1px solid #ddd;
            margin-bottom: 5px;
            padding: 10px;
            cursor: pointer;
        }
        #notesList li:hover {
            background: #e9e9e9;
        }
        form {
            margin-top: 20px;
            display: flex;
            flex-direction: column;
        }
        input[type="text"], textarea {
            margin-bottom: 10px;
            padding: 8px;
            font-size: 16px;
        }
        button {
            width: 100px;
            padding: 8px;
            font-size: 16px;
            cursor: pointer;
        }
        .buttons {
            display: flex;
            gap: 10px;
        }
    </style>
</head>
<body>
    <h1>Заметки</h1>
    <ul id="notesList"></ul>

    <form id="noteForm">
        <input type="text" id="noteTitle" placeholder="Заголовок" required />
        <textarea id="noteContent" placeholder="Содержание" rows="5" required></textarea>
        <div class="buttons">
            <button type="submit">Сохранить</button>
            <button type="button" id="deleteBtn" style="display:none">Удалить</button>
            <button type="button" id="cancelBtn" style="display:none">Отмена</button>
        </div>
    </form>

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

Логика работы на JavaScript

Создайте файл public/app.js, в котором реализуйте работу с API и управление формой и списком заметок:

document.addEventListener('DOMContentLoaded', () => {
    const notesList = document.getElementById('notesList');
    const noteForm = document.getElementById('noteForm');
    const titleInput = document.getElementById('noteTitle');
    const contentInput = document.getElementById('noteContent');
    const deleteBtn = document.getElementById('deleteBtn');
    const cancelBtn = document.getElementById('cancelBtn');

    let selectedNoteId = null;

    // Загрузка заметок с сервера
    async function loadNotes() {
        const res = await fetch('/api/notes');
        const notes = await res.json();
        notesList.innerHTML = '';
        notes.forEach(note => {
            const li = document.createElement('li');
            li.textContent = note.title;
            li.dataset.id = note.id;
            notesList.appendChild(li);
        });
    }

    // Очистка формы
    function clearForm() {
        titleInput.value = '';
        contentInput.value = '';
        deleteBtn.style.display = 'none';
        cancelBtn.style.display = 'none';
        selectedNoteId = null;
    }

    // Отображение заметки в форме
    async function showNote(id) {
        const res = await fetch(`/api/notes/${id}`);
        if (res.ok) {
            const note = await res.json();
            titleInput.value = note.title;
            contentInput.value = note.content;
            selectedNoteId = note.id;
            deleteBtn.style.display = 'inline-block';
            cancelBtn.style.display = 'inline-block';
        }
    }

    // Обработка клика на заметку
    notesList.addEventListener('click', e => {
        if (e.target.tagName === 'LI') {
            showNote(e.target.dataset.id);
        }
    });

    // Обработка отправки формы
    noteForm.addEventListener('submit', async e => {
        e.preventDefault();
        const title = titleInput.value.trim();
        const content = contentInput.value.trim();

        if (!title || !content) {
            alert('Заполните заголовок и содержание');
            return;
        }

        if (selectedNoteId) {
            // Обновление заметки
            const res = await fetch(`/api/notes/${selectedNoteId}`, {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ title, content })
            });
            if (res.ok) {
                await loadNotes();
                clearForm();
            } else {
                alert('Ошибка при обновлении заметки');
            }
        } else {
            // Создание новой заметки
            const res = await fetch('/api/notes', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ title, content })
            });
            if (res.ok) {
                await loadNotes();
                clearForm();
            } else {
                alert('Ошибка при создании заметки');
            }
        }
    });

    // Обработка удаления заметки
    deleteBtn.addEventListener('click', async () => {
        if (selectedNoteId && confirm('Удалить заметку?')) {
            const res = await fetch(`/api/notes/${selectedNoteId}`, { method: 'DELETE' });
            if (res.ok) {
                await loadNotes();
                clearForm();
            } else {
                alert('Ошибка при удалении заметки');
            }
        }
    });

    // Отмена редактирования
    cancelBtn.addEventListener('click', () => {
        clearForm();
    });

    // Первичная загрузка заметок
    loadNotes();
});

Этот скрипт обеспечивает динамическое взаимодействие с серверным API: загрузка заметок, отображение данных, создание, обновление и удаление. Пользовательский опыт упрощён за счёт мгновенного обновления списка без перезагрузки страницы.

Особенности и улучшения

Рассмотренное приложение — базовый пример, демонстрирующий программирование REST API на Express.js и взаимодействие с клиентской частью через Fetch API. В реальной практике проект можно расширять и улучшать различными способами.

К основным направлениям развития относятся:

  • Автентификация и авторизация пользователей. Добавление регистрации и входа, чтобы каждый пользователь имел свои заметки;
  • Хранение данных в базе данных, например MongoDB или PostgreSQL, для обеспечения масштабируемости, производительности и безопасности;
  • Использование шаблонизаторов или frontend-фреймворков (React, Vue, Angular) для создания более интерактивного и удобного интерфейса;
  • Валидация и обработка ошибок на уровне сервера и клиента для повышения стабильности;
  • Реализовать поиск и сортировку заметок, улучшить UX;
  • Добавить резервное копирование и синхронизацию, чтобы избегать потери данных;
  • Обеспечить безопасность (например, защиту от XSS, CSRF, настроить CORS и HTTPS).

Заключение

В данной статье мы подробно рассмотрели процесс создания простого веб-приложения для хранения заметок с использованием Express.js. Мы разобрали этапы планирования, настройки проекта, разработки REST API для управления заметками и создания клиентской части с помощью базовых веб-технологий. Такой подход прекрасно подходит для понимания принципов работы серверных приложений на Node.js и организации взаимодействия между клиентом и сервером.

Созданный пример можно легко адаптировать и расширять в зависимости от конкретных целей и требований проекта. Express.js остаётся одним из наиболее удобных и мощных инструментов для разработки веб-приложений и API, что делает его хорошим выбором как для начинающих, так и для опытных разработчиков.

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



Вот HTML-таблица с 10 LSI-запросами для статьи "Разработка веб-приложения для хранения заметок с использованием Express.js":

```html

Запрос 1 Запрос 2 Запрос 3 Запрос 4 Запрос 5
Создание заметок с Express.js Веб-приложение для заметок Основы Express.js для начинающих Хранение данных в веб-приложении REST API на Express.js
Доступ к заметкам на JavaScript Интерфейс заметок с React Сохранение данных в MongoDB Обработка форм в Express.js Аутентификация пользователей в приложениях

```

Это простой HTML-код, который можно использовать для создания таблицы с запросами. Каждая ячейка таблицы содержит ссылку на соответствующий запрос.