Quest: Node.js API z prostym frontendem React

Wstęp

Zadanie polega na zbudowaniu, uruchomieniu i przetestowaniu prostego CRUD API oraz uruchomieniu frontendu testowego w React. Załóżmy że chcemy przechowywać w bazie danych informacje o produktach (nazwa, opis, cena).
Użyjemy Node.js + Express.js + MySQL + mysql. Dane będziemy przekazywać i odbierać jako json.

Prosty serwer na Node

Zainstaluj wymagane zależności:

cd katalog
npm init -y
npm install express mysql cors

Utwórz bazę w MySQL (np w XAMPP przez PhpMyAdmin)
Stwórz i uruchom plik app.js z następującą zawartością:

const express = require('express');
const mysql = require('mysql');
const cors = require('cors');

// Tworzenie aplikacji Express
const app = express();

// Dzieki tej bibliotece będzie łatwiej. To takie obejście dla Same-Origin Policy w przeglądarce.
app.use(cors());

// Middleware do parsowania JSON (Express ma wbudowany parser JSON od wersji 4.16)
app.use(express.json());

// Konfiguracja połączenia z bazą danych
const db = mysql.createConnection({
    host: 'localhost',
    user: 'root', // Zmień na swojego użytkownika
    password: '', // Ustaw swoje hasło
    database: 'products_db', // Ustaw nazwę swojej bazy danych
});

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

// Tworzenie tabeli w bazie danych (jeśli nie istnieje)
const createTableQuery = `
    CREATE TABLE IF NOT EXISTS products (
        id INT AUTO_INCREMENT PRIMARY KEY,
        name VARCHAR(255) NOT NULL,
        description TEXT,
        price DECIMAL(10, 2) NOT NULL
    );
`;
db.query(createTableQuery, (err) => {
    if (err) throw err;
    console.log('Tabela "products" została utworzona.');
});

// Endpointy API

// 1. Pobierz wszystkie produkty
app.get('/products', (req, res) => {
    const query = 'SELECT * FROM products';
    db.query(query, (err, results) => {
        if (err) {
            res.status(500).send(err.message);
            return;
        }
        res.json(results);
    });
});

// 2. Pobierz produkt po ID
app.get('/products/:id', (req, res) => {
    const query = 'SELECT * FROM products WHERE id = ?';
    db.query(query, [req.params.id], (err, results) => {
        if (err) {
            res.status(500).send(err.message);
            return;
        }
        if (results.length === 0) {
            res.status(404).send('Produkt nie został znaleziony.');
            return;
        }
        res.json(results[0]);
    });
});

// 3. Dodaj nowy produkt
app.post('/products', (req, res) => {
    const { name, description, price } = req.body;
    const query = 'INSERT INTO products (name, description, price) VALUES (?, ?, ?)';
    db.query(query, [name, description, price], (err, result) => {
        if (err) {
            res.status(500).send(err.message);
            return;
        }
        res.status(201).send({ id: result.insertId, name, description, price });
    });
});

// 4. Aktualizuj produkt
app.put('/products/:id', (req, res) => {
    const { name, description, price } = req.body;
    const query = 'UPDATE products SET name = ?, description = ?, price = ? WHERE id = ?';
    db.query(query, [name, description, price, req.params.id], (err, result) => {
        if (err) {
            res.status(500).send(err.message);
            return;
        }
        if (result.affectedRows === 0) {
            res.status(404).send('Produkt nie został znaleziony.');
            return;
        }
        res.send('Produkt został zaktualizowany.');
    });
});

// 5. Usuń produkt
app.delete('/products/:id', (req, res) => {
    const query = 'DELETE FROM products WHERE id = ?';
    db.query(query, [req.params.id], (err, result) => {
        if (err) {
            res.status(500).send(err.message);
            return;
        }
        if (result.affectedRows === 0) {
            res.status(404).send('Produkt nie został znaleziony.');
            return;
        }
        res.send('Produkt został usunięty.');
    });
});

// Uruchomienie serwera
const PORT = 3000;
app.listen(PORT, () => {
    console.log(`Serwer działa na porcie ${PORT}`);
});

Testowanie API

Użyj gotowych narzędzi do testowania API. Proponuję Postman. W wersji bezpłatnej, bez zakładania konta, można wykonać wszystkie testy. Aby na przykład dodać dane do bazy za pomocą Postman:

  • Wybierz metodę POST.
  • Wprowadź URL: http://localhost:3000/products.
  • Przejdź do zakładki Body, wybierz opcję raw i jako format wybierz JSON. Wklej:
    { "name": "Produkt A", "description": "Opis produktu A", "price": 99.99 }
  • Kliknij Send.

Frontend w React

Kolejny krok to uruchomienie aplikacji React wykorzystującej nasze API.
Najpierw trzeba przygotować środowisko:

cd /home/piotr/sand/react_frontend_4_simple_api
npm create vite@latest my-app --template react
cd my-app
npm install
npm install axios
npm install bootstrap
npm run dev

Skopiuj poniższy kod do src/App.jsx

import { useEffect, useState } from 'react';
import axios from 'axios';
import 'bootstrap/dist/css/bootstrap.min.css';

const API_URL = 'http://localhost:3000/products';

function App() {
  const [products, setProducts] = useState([]);
  const [name, setName] = useState('');
  const [description, setDescription] = useState('');
  const [price, setPrice] = useState('');

  useEffect(() => {
    fetchProducts();
  }, []);

  const fetchProducts = async () => {
    try {
      const response = await axios.get(API_URL);
      setProducts(response.data);
    } catch (error) {
      console.error('Błąd podczas pobierania produktów:', error);
    }
  };

  const addProduct = async () => {
    if (!name || !price) return;
    try {
      await axios.post(API_URL, { name, description, price });
      setName('');
      setDescription('');
      setPrice('');
      fetchProducts();
    } catch (error) {
      console.error('Błąd podczas dodawania produktu:', error);
    }
  };

  return (
    <div className="container mt-5">
      <h2 className="mb-4">Lista Produktów</h2>
      <ul className="list-group mb-4">
        {products.map((product) => (
          <li key={product.id} className="list-group-item">
            <strong>{product.name}</strong> - {product.description} - ${product.price}
          </li>
        ))}
      </ul>
      <h3>Dodaj Produkt</h3>
      <div className="mb-3">
        <input type="text" className="form-control" placeholder="Nazwa" value={name} onChange={(e) => setName(e.target.value)} />
      </div>
      <div className="mb-3">
        <input type="text" className="form-control" placeholder="Opis" value={description} onChange={(e) => setDescription(e.target.value)} />
      </div>
      <div className="mb-3">
        <input type="number" className="form-control" placeholder="Cena" value={price} onChange={(e) => setPrice(e.target.value)} />
      </div>
      <button className="btn btn-primary" onClick={addProduct}>Dodaj Produkt</button>
    </div>
  );
}

export default App;

Zadanie na 4 i 5

Dodaj kolejne endpointy i kolejną tabelę w bazie (np. z zakupami i sprzedażami).
Dodaj obsługę tych endpointów w apce reactowej (prostą).

Jedna odpowiedź

Skomentuj Klovy Anuluj pisanie odpowiedzi

Zapisz moje dane, adres e-mail i witrynę w przeglądarce aby wypełnić dane podczas pisania kolejnych komentarzy.