Ładowanie

Node.js + JS + Express – MYSQL

Spis Treści

1. Instalacja Niezbędnych Pakietów

Aby rozpocząć tworzenie aplikacji, upewnij się, że masz zainstalowany Node.js oraz npm (Node Package Manager). Node.js jest środowiskiem uruchomieniowym, które pozwala na korzystanie z JavaScript poza przeglądarką, natomiast npm umożliwia zarządzanie pakietami. Jeśli jeszcze nie masz Node.js, pobierz go ze strony nodejs.org.

Po instalacji Node.js i npm, utwórz nowy katalog projektu i zainicjuj go, wykonując poniższe polecenia:

mkdir my-express-mysql-app
cd my-express-mysql-app
npm init -y

Powyższe komendy utworzą katalog projektu i zainicjują plik package.json, który będzie zawierał informacje o projekcie i zależnościach.

Zainstaluj wymagane pakiety:

npm install express mysql cors

Powyższa komenda instaluje trzy pakiety:

  • express: framework do tworzenia serwera aplikacji
  • mysql: pakiet do komunikacji z bazą danych MySQL
  • cors: middleware do obsługi CORS (Cross-Origin Resource Sharing), który umożliwia dostęp do serwera z innych domen

Opcjonalnie możesz zainstalować nodemon, aby automatycznie restartował serwer przy każdej zmianie w kodzie:

npm install --save-dev nodemon

W package.json możesz dodać skrypt do uruchamiania serwera z nodemon:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

2. Konfiguracja Bazy Danych MySQL

Stwórz bazę danych w MySQL, która będzie przechowywać dane o przedmiotach w naszym magazynie. Załóżmy, że chcemy przechowywać przedmioty z polami: id, name, quantity, price.

Otwórz terminal MySQL i wykonaj poniższe komendy, aby stworzyć bazę danych i tabelę:

CREATE DATABASE inventory_db;
USE inventory_db;

CREATE TABLE items (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  quantity INT NOT NULL,
  price DECIMAL(10,2) NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Stworzyliśmy tabelę items z czterema kolumnami:

  • id: unikalny identyfikator przedmiotu (klucz główny)
  • name: nazwa przedmiotu
  • quantity: ilość przedmiotu w magazynie
  • price: cena przedmiotu
  • created_at: data i czas dodania przedmiotu

3. Tworzenie Serwera Express.js

Teraz utworzymy serwer aplikacji, który pozwoli na interakcję z naszą bazą danych. Stwórz plik server.js w katalogu projektu i dodaj poniższy kod:

// server.js
const express = require('express');
const mysql = require('mysql');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

// Konfiguracja MySQL
const db = mysql.createConnection({
  host: 'localhost',
  user: 'your_username',
  password: 'your_password',
  database: 'inventory_db'
});

db.connect((err) => {
  if (err) {
    console.error('Błąd połączenia z MySQL:', err);
    return;
  }
  console.log('Połączono z bazą danych MySQL');
});

// API endpoints (POST, GET, PUT, DELETE) tutaj...

W powyższym kodzie importujemy potrzebne moduły (express, mysql, cors) i konfigurujemy połączenie z bazą danych MySQL. Serwer obsługuje JSON w treści żądań i umożliwia komunikację z front-endem (dzięki middleware cors()).

4. Tworzenie Interfejsu Użytkownika (HTML)

Tworzymy prosty interfejs użytkownika, który pozwoli na zarządzanie przedmiotami w magazynie. Utwórz folder public i dodaj plik index.html z poniższym kodem:


  

Inventory Management

ID Nazwa Ilość Cena Akcje

Powyższy kod tworzy formularz do dodawania nowych przedmiotów oraz tabelę do wyświetlania przedmiotów znajdujących się w magazynie. Interfejs będzie również zawierał skrypt JavaScript do obsługi formularza i aktualizacji tabeli.

5. Uruchomienie Aplikacji

Aby uruchomić aplikację, najpierw upewnij się, że serwer MySQL jest włączony, a następnie uruchom serwer aplikacji. W terminalu przejdź do katalogu projektu i wykonaj polecenie:

npm start

Jeśli korzystasz z nodemon, możesz uruchomić serwer komendą:

npm run dev

Po uruchomieniu serwera, otwórz przeglądarkę i przejdź pod adres http://localhost:3000, aby zobaczyć interfejs użytkownika aplikacji.

6. Dodatkowe Uwagi

  • Zapobieganie SQL Injection: W zapytaniach SQL używamy ? jako placeholderów, co pomaga zapobiegać atakom SQL Injection poprzez automatyczne zastąpienie danych wejściowych. Nigdy nie wstawiaj danych bezpośrednio do zapytań SQL bez walidacji.
  • Bezpieczeństwo Połączenia: Rozważ użycie zmiennych środowiskowych do przechowywania poufnych informacji, takich jak hasła do bazy danych. Możesz użyć pakietu dotenv do wczytywania zmiennych z pliku .env.
  • Użycie ORM: Jeśli preferujesz wyższy poziom abstrakcji, zamiast SQL możesz użyć ORM (Object-Relational Mapping), jak Sequelize lub TypeORM, co upraszcza operacje na bazie danych.
  • Walidacja Danych: Implementuj bardziej zaawansowaną walidację danych wejściowych, aby zapewnić integralność i bezpieczeństwo danych. Możesz użyć bibliotek takich jak Joi lub express-validator.
  • Obsługa CORS: W przykładzie użyliśmy pakietu cors, aby umożliwić komunikację między front-endem a serwerem. Możesz dostosować ustawienia CORS, aby ograniczyć dostęp tylko do określonych domen.
				
					<!DOCTYPE html>
<html lang="pl">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Inventory Management</title>
  <style>
    body {
      font-family: Arial, sans-serif;
      line-height: 1.6;
      margin: 20px;
    }
    h1 {
      text-align: center;
    }
    form {
      margin-bottom: 20px;
    }
    input, button {
      padding: 8px;
      margin: 5px;
    }
    table {
      width: 100%;
      border-collapse: collapse;
      margin-top: 20px;
    }
    th, td {
      border: 1px solid #ddd;
      padding: 12px;
      text-align: center;
    }
    th {
      background-color: #f2f2f2;
    }
    .delete-button {
      background-color: #ff4d4d;
      color: white;
      border: none;
      padding: 5px 10px;
      cursor: pointer;
    }
    .delete-button:hover {
      background-color: #ff1a1a;
    }
  </style>
</head>
<body>

  <h1>Inventory Management</h1>

  <!-- Formularz dodawania nowego przedmiotu -->
  <form id="item-form">
    <input type="text" id="name" placeholder="Nazwa przedmiotu" required>
    <input type="number" id="quantity" placeholder="Ilość" required>
    <input type="number" step="0.01" id="price" placeholder="Cena" required>
    <button type="submit">Dodaj Przedmiot</button>
  </form>

  <!-- Tabela do wyświetlania przedmiotów -->
  <table id="items-table">
    <thead>
      <tr>
        <th>ID</th>
        <th>Nazwa</th>
        <th>Ilość</th>
        <th>Cena</th>
        <th>Akcje</th>
      </tr>
    </thead>
    <tbody>
      <!-- Przedmioty będą tutaj dodawane dynamicznie -->
    </tbody>
  </table>

  <script>
    const form = document.getElementById('item-form');
    const itemsTableBody = document.querySelector('#items-table tbody');

    // Funkcja do pobierania wszystkich przedmiotów z serwera
    async function fetchItems() {
      try {
        const response = await fetch('/items');
        if (!response.ok) throw new Error("Błąd w trakcie pobierania przedmiotów");
        
        const items = await response.json();
        itemsTableBody.innerHTML = '';
        items.forEach(item => {
          const row = document.createElement('tr');
          row.innerHTML = `
            <td>${item.id}</td>
            <td>${item.name}</td>
            <td>${item.quantity}</td>
            <td>${item.price.toFixed(2)}</td>
            <td><button class="delete-button" data-id="${item.id}">Usuń</button></td>
          `;
          itemsTableBody.appendChild(row);
        });
      } catch (error) {
        console.error('Błąd podczas pobierania przedmiotów:', error);
      }
    }

    // Obsługa wysyłania formularza dodawania nowego przedmiotu
    form.addEventListener('submit', async (e) => {
      e.preventDefault();

      // Pobieranie danych z formularza
      const name = document.getElementById('name').value.trim();
      const quantity = parseInt(document.getElementById('quantity').value);
      const price = parseFloat(document.getElementById('price').value);

      // Walidacja danych
      if (!name || isNaN(quantity) || isNaN(price)) {
        alert('Proszę wypełnić wszystkie pola poprawnie.');
        return;
      }

      try {
        const response = await fetch('/items', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({ name, quantity, price })
        });

        if (response.ok) {
          const result = await response.json();
          alert(result.message);
          form.reset();
          fetchItems();  // Odśwież listę przedmiotów po dodaniu nowego
        } else {
          const error = await response.json();
          alert(error.message);
        }
      } catch (error) {
        console.error('Błąd podczas dodawania przedmiotu:', error);
      }
    });

    // Obsługa usuwania przedmiotów
    itemsTableBody.addEventListener('click', async (e) => {
      if (e.target.classList.contains('delete-button')) {
        const itemId = e.target.getAttribute('data-id');
        if (confirm('Czy na pewno chcesz usunąć ten przedmiot?')) {
          try {
            const response = await fetch(`/items/${itemId}`, {
              method: 'DELETE'
            });

            if (response.ok) {
              const result = await response.json();
              alert(result.message);
              fetchItems();  // Odśwież listę przedmiotów po usunięciu
            } else {
              const error = await response.json();
              alert(error.message);
            }
          } catch (error) {
            console.error('Błąd podczas usuwania przedmiotu:', error);
          }
        }
      }
    });

    // Pobierz wszystkie przedmioty przy załadowaniu strony
    fetchItems();
  </script>

</body>
</html>

				
			
				
					// server.js
const express = require('express');
const mysql = require('mysql');
const cors = require('cors');

const app = express();
app.use(cors());
app.use(express.json());

// MySQL Configuration
const db = mysql.createConnection({
  host: 'localhost',
  user: 'your_username',  // replace with your MySQL username
  password: 'your_password',  // replace with your MySQL password
  database: 'inventory_db'  // replace with your MySQL database name
});

// Connect to MySQL
db.connect((err) => {
  if (err) {
    console.error('Error connecting to MySQL:', err);
    return;
  }
  console.log('Connected to MySQL database');
});

// Endpoint to fetch all items (GET)
app.get('/items', (req, res) => {
  const query = 'SELECT * FROM items';
  db.query(query, (err, results) => {
    if (err) {
      console.error('Error fetching items:', err);
      return res.status(500).json({ message: 'Error fetching items' });
    }
    res.json(results);
  });
});

// Endpoint to add a new item (POST)
app.post('/items', (req, res) => {
  const { name, quantity, price } = req.body;
  if (!name || quantity <= 0 || price <= 0) {
    return res.status(400).json({ message: 'Invalid item data' });
  }

  const query = 'INSERT INTO items (name, quantity, price) VALUES (?, ?, ?)';
  db.query(query, [name, quantity, price], (err, result) => {
    if (err) {
      console.error('Error adding item:', err);
      return res.status(500).json({ message: 'Error adding item' });
    }
    res.json({ message: 'Item added successfully', id: result.insertId });
  });
});

// Endpoint to delete an item by ID (DELETE)
app.delete('/items/:id', (req, res) => {
  const { id } = req.params;
  const query = 'DELETE FROM items WHERE id = ?';
  db.query(query, [id], (err, result) => {
    if (err) {
      console.error('Error deleting item:', err);
      return res.status(500).json({ message: 'Error deleting item' });
    }
    if (result.affectedRows === 0) {
      return res.status(404).json({ message: 'Item not found' });
    }
    res.json({ message: 'Item deleted successfully' });
  });
});

// Start the server
const PORT = 3000;
app.listen(PORT, () => {
  console.log(`Server running on http://localhost:${PORT}`);
});