Пошаговое руководство по созданию базы данных и бэкенда для веб-сайта товаров и услуг
Комплексное решение с использованием PostgreSQL и Node.js для эффективного управления
Ключевые выводы
- Проектирование базы данных: Четкая структура с основными сущностями и связями обеспечивает надежное хранение данных.
- Безопасность и масштабируемость: Использование современных технологий и методов защиты гарантирует безопасность и возможность роста приложения.
- Эффективная реализация бэкенда: Node.js и Express обеспечивают высокую производительность и гибкость при разработке серверной части.
1. Проектирование базы данных
Определение сущностей и их связей
Первым шагом в создании базы данных является определение основных сущностей и их взаимосвязей. Для веб-сайта, предоставляющего товары и услуги, необходимо учесть следующие ключевые таблицы:
Основные таблицы
| Таблица | Описание |
|---|---|
| users | Хранит информацию о пользователях, включая авторизацию и роли. |
| categories | Содержит категории и подкатегории товаров и услуг. |
| products | Хранит данные о товарах, включая описание, цену и фото. |
| services | Содержит информацию об услугах, описания и примеры проектов. |
| projects | Примеры выполненных проектов с фотографиями и комментариями. |
| comments | Комментарии пользователей к проектам. |
| orders | Информация о заказах пользователей, включая статус и сумму. |
| order_items | Детали заказов, связывающие продукты и услуги с конкретными заказами. |
Диаграмма сущностей
Для лучшего понимания структуры базы данных рекомендуется создать диаграмму сущностей, которая визуализирует связи между таблицами. Это поможет избежать ошибок при разработке и упростит дальнейшее обслуживание системы.
2. Создание базы данных в PostgreSQL
Определение и создание таблиц
Используя SQL, создаем необходимые таблицы с учетом связей и необходимых ограничений.
Создание таблиц
-- Пользователи
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
role VARCHAR(50) DEFAULT 'user',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Категории
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
parent_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
type VARCHAR(50) NOT NULL
);
-- Товары
CREATE TABLE products (
id SERIAL PRIMARY KEY,
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2) NOT NULL,
image_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Услуги
CREATE TABLE services (
id SERIAL PRIMARY KEY,
category_id INTEGER REFERENCES categories(id) ON DELETE SET NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10, 2),
image_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Примеры проектов
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
service_id INTEGER REFERENCES services(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
image_url TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Комментарии
CREATE TABLE comments (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE,
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Заказы
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE SET NULL,
total_amount DECIMAL(10, 2) NOT NULL,
status VARCHAR(50) DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Элементы заказа
CREATE TABLE order_items (
id SERIAL PRIMARY KEY,
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
product_id INTEGER REFERENCES products(id) ON DELETE SET NULL,
service_id INTEGER REFERENCES services(id) ON DELETE SET NULL,
quantity INTEGER NOT NULL,
price DECIMAL(10, 2) NOT NULL
);
3. Настройка Node.js и Express
Установка зависимостей и настройка сервера
Для создания бэкенда используем Node.js с фреймворком Express и подключением к PostgreSQL через библиотеку pg. Также применяем bcrypt для хэширования паролей и JSON Web Tokens (JWT) для аутентификации.
Инициализация проекта и установка зависимостей
npm init -y
npm install express pg bcrypt jsonwebtoken dotenv cors helmet express-rate-limit
Создание структуры проекта
ecommerce-backend/
├── .env
├── app.js
├── config/
│ └── db.js
├── models/
│ ├── userModel.js
│ ├── productModel.js
│ └── serviceModel.js
├── routes/
│ ├── authRoutes.js
│ ├── productRoutes.js
│ └── serviceRoutes.js
└── controllers/
├── authController.js
├── productController.js
└── serviceController.js
Настройка соединения с базой данных
const { Pool } = require('pg');
const dotenv = require('dotenv');
dotenv.config();
const pool = new Pool({
user: process.env.DB_USER,
host: process.env.DB_HOST,
database: process.env.DB_NAME,
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT,
});
module.exports = pool;
Настройка сервера Express
const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const dotenv = require('dotenv');
const authRoutes = require('./routes/authRoutes');
const productRoutes = require('./routes/productRoutes');
const serviceRoutes = require('./routes/serviceRoutes');
dotenv.config();
const app = express();
// Middleware для безопасности
app.use(helmet());
app.use(cors());
app.use(express.json());
// Ограничение запросов
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 минут
max: 100 // максимум 100 запросов
});
app.use(limiter);
// Маршруты
app.use('/api/auth', authRoutes);
app.use('/api/products', productRoutes);
app.use('/api/services', serviceRoutes);
// Обработка ошибок
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Что-то пошло не так!' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Сервер запущен на порту ${PORT}`);
});
4. Реализация аутентификации пользователей
Регистрация и авторизация с использованием JWT
Для обеспечения безопасности доступа к ресурсам веб-сайта реализуем регистрацию и авторизацию пользователей с помощью JWT.
Контроллеры аутентификации
// controllers/authController.js
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const pool = require('../config/db');
const register = async (req, res) => {
const { username, email, password } = req.body;
try {
const hashedPassword = await bcrypt.hash(password, 10);
const result = await pool.query(
'INSERT INTO users (username, email, password_hash) VALUES ($1, $2, $3) RETURNING *',
[username, email, hashedPassword]
);
res.status(201).json(result.rows[0]);
} catch (error) {
res.status(500).json({ error: 'Ошибка при регистрации пользователя' });
}
};
const login = async (req, res) => {
const { email, password } = req.body;
try {
const userResult = await pool.query('SELECT * FROM users WHERE email = $1', [email]);
if (userResult.rows.length === 0) {
return res.status(400).json({ error: 'Неверный email или пароль' });
}
const user = userResult.rows[0];
const validPassword = await bcrypt.compare(password, user.password_hash);
if (!validPassword) {
return res.status(400).json({ error: 'Неверный email или пароль' });
}
const token = jwt.sign(
{ id: user.id, role: user.role },
process.env.JWT_SECRET,
{ expiresIn: '1h' }
);
res.json({ token });
} catch (error) {
res.status(500).json({ error: 'Ошибка при аутентификации пользователя' });
}
};
module.exports = { register, login };
Маршруты аутентификации
// routes/authRoutes.js
const express = require('express');
const { register, login } = require('../controllers/authController');
const router = express.Router();
router.post('/register', register);
router.post('/login', login);
module.exports = router;
Мидлвар для проверки JWT
// middleware/authMiddleware.js
const jwt = require('jsonwebtoken');
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token == null) return res.status(401).json({ error: 'Токен не предоставлен' });
jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
if (err) return res.status(403).json({ error: 'Неверный токен' });
req.user = user;
next();
});
};
module.exports = authenticateToken;
5. Реализация функционала заказов
Создание и управление заказами товаров и услуг
Позволяет пользователям создавать заказы на товары и услуги, обеспечивая связь между пользователями и их покупками или заказанными услугами.
Контроллеры заказов
// controllers/orderController.js
const pool = require('../config/db');
const createOrder = async (req, res) => {
const { items } = req.body;
const userId = req.user.id;
try {
await pool.query('BEGIN');
const totalAmount = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const orderResult = await pool.query(
'INSERT INTO orders (user_id, total_amount) VALUES ($1, $2) RETURNING *',
[userId, totalAmount]
);
const orderId = orderResult.rows[0].id;
for (const item of items) {
await pool.query(
'INSERT INTO order_items (order_id, product_id, service_id, quantity, price) VALUES ($1, $2, $3, $4, $5)',
[orderId, item.product_id || null, item.service_id || null, item.quantity, item.price]
);
}
await pool.query('COMMIT');
res.status(201).json(orderResult.rows[0]);
} catch (error) {
await pool.query('ROLLBACK');
res.status(500).json({ error: 'Ошибка при создании заказа' });
}
};
const getOrders = async (req, res) => {
const userId = req.user.id;
try {
const orders = await pool.query('SELECT * FROM orders WHERE user_id = $1', [userId]);
res.json(orders.rows);
} catch (error) {
res.status(500).json({ error: 'Ошибка при получении заказов' });
}
};
module.exports = { createOrder, getOrders };
Маршруты заказов
// routes/orderRoutes.js
const express = require('express');
const { createOrder, getOrders } = require('../controllers/orderController');
const authenticateToken = require('../middleware/authMiddleware');
const router = express.Router();
router.post('/', authenticateToken, createOrder);
router.get('/', authenticateToken, getOrders);
module.exports = router;
6. Обеспечение безопасности и масштабируемости
Применение лучших практик для защиты данных и увеличения производительности
Для обеспечения безопасности и масштабируемости необходимо внедрить несколько ключевых практик в процессе разработки и эксплуатации приложения.
Безопасность
- Использование HTTPS для защиты данных при передаче.
- Хэширование паролей с помощью bcrypt для защиты учетных записей.
- Валидация входных данных для предотвращения SQL-инъекций и XSS-атак.
- Ограничение доступа к API с помощью ролей пользователей.
- Применение Helmet для добавления HTTP-заголовков безопасности.
- Использование CORS для контроля доступа к ресурсам API.
- Ограничение количества запросов с помощью express-rate-limit для предотвращения DDoS-атак.
Масштабируемость
- Использование пулов соединений PostgreSQL для эффективного управления подключениями.
- Внедрение кеширования с помощью Redis для ускорения доступа к часто запрашиваемым данным.
- Оптимизация SQL-запросов и создание индексов на часто используемых полях.
- Горизонтальное масштабирование приложения с использованием контейнеров Docker и оркестраторов, таких как Kubernetes.
- Использование балансировщиков нагрузки для распределения трафика между серверами.
7. Тестирование и развертывание
Обеспечение стабильности и доступности приложения
После разработки необходимо тщательно протестировать приложение и подготовить его к развертыванию на продакшн-сервере.
Тестирование
- Использование инструментов, таких как Postman, для тестирования API эндпоинтов.
- Написание модульных и интеграционных тестов с помощью Jest или Mocha.
- Проведение нагрузочного тестирования для оценки производительности под высокой нагрузкой.
Развертывание
-
Использование Docker для контейнеризации приложения, что обеспечивает консистентность окружения.
-
Развертывание на облачных платформах, таких как AWS, Heroku или DigitalOcean.
-
Настройка CI/CD процессов для автоматического тестирования и развертывания при изменениях кода.
-
Мониторинг приложения с помощью инструментов, таких как Prometheus и Grafana, для отслеживания производительности и обнаружения проблем.
Заключение
Создание базы данных и бэкенда для веб-сайта, предоставляющего товары и услуги, требует тщательного планирования и реализации. В этом руководстве мы рассмотрели все ключевые этапы, начиная от проектирования структуры базы данных до обеспечения безопасности и масштабируемости приложения. Следуя представленным шагам и внедряя лучшие практики, вы сможете разработать надежный и эффективный веб-сайт, способный удовлетворить потребности пользователей и обеспечить стабильную работу в долгосрочной перспективе.
Ресурсы и ссылки
Last updated January 20, 2025