Projekt
końcowy w ExpressJS jest doskonałą okazją do zrozumienia, jak budować i integrować różne elementy pełnej aplikacji. W tym wpisie krok po kroku poprowadzę Cię przez tworzenie prostego API do zarządzania zadaniami, stosując wszystkie poznane wcześniej koncepcje, jak middleware, kontrolery, oraz Docker do konteneryzacji.
Cele projektu
- Zastosowanie poznanych koncepcji w praktyce.
- Stworzenie API do zarządzania zadaniami (CRUD).
- Zrozumienie, jak zintegrować różne elementy ExpressJS w pełnej aplikacji.
- Stworzenie zautomatyzowanego środowiska przy użyciu Dockera.
Struktura projektu
Twoja aplikacja ma następującą strukturę:
express-todo-app/
├── app/
│ ├── config/
│ │ └── db.js
│ ├── controllers/
│ │ └── tasksController.js
│ ├── middlewares/
│ │ └── authMiddleware.js
│ ├── routes/
│ │ └── tasksRoutes.js
│ └── index.js
├── docker/
│ ├── mysql/
│ │ └── 01-schema.sql
│ ├── Dockerfile_Mysql
│ └── Dockerfile_NodeJs
├── .env
├── docker-compose.yaml
├── package.json
└── README.md
Ta struktura pomaga w modularizacji kodu, co ułatwia rozwój, testowanie i utrzymanie aplikacji.
Konfiguracja .env
Twój plik .env zawiera niezbędne zmienne środowiskowe do uruchomienia aplikacji:
APP_NAME=express-todo-app
DB_CONNECTION=mysql
DOCKER_DOMAIN=${APP_NAME}
DOCKER_NODE_PORT=10060
DOCKER_MYSQL_PORT=10062
DOCKER_MYSQL_NAME=todoapp
DOCKER_MYSQL_USER=user
DOCKER_MYSQL_PASS=pass
DOCKER_MYSQL_ROOT=rootpass
DOCKER_PHPMYADMIN_PORT=10063
APP_JWT_SECRET=loremipsum
Wartości w .env są używane w aplikacji oraz w konfiguracji Docker, co zapewnia spójność i łatwość dostosowywania do różnych środowisk.
Konfiguracja projektu
Zacznijmy od opisu kluczowych elementów projektu, począwszy od pliku package.json:
{"name":"express-todo-app","version":"1.0.0","main":"index.js","dependencies":{"bcryptjs":"^2.4.3","body-parser":"^1.19.0","dotenv":"^8.2.0","express":"^4.17.1","jsonwebtoken":"^8.5.1","mysql2":"^1.7.0"},"scripts":{"start":"node index.js","dev":"nodemon index.js"},"devDependencies":{"nodemon":"^1.19.4"},"author":"","license":"ISC","description":""}
Konfiguracja bazy danych – db.js
Plik db.js w katalogu app/config/ odpowiada za połączenie z bazą danych MySQL:
const mysql = require('mysql2');
const connection = mysql.createConnection({
host: process.env.DB_CONNECTION || 'localhost',
user: process.env.DOCKER_MYSQL_USER,
password: process.env.DOCKER_MYSQL_PASS,
database: process.env.DOCKER_MYSQL_NAME
});
module.exports = connection;
Konfiguracja korzysta z wartości z pliku .env, co pozwala łatwo zmieniać ustawienia połączenia bez konieczności modyfikacji kodu źródłowego.
Kontroler – tasksController.js
Plik tasksController.js w katalogu app/controllers/ zawiera logikę CRUD:
const db = require('../config/db');
exports.getAllTasks = (req, res) => {
db.query('SELECT * FROM tasks', (err, results) => {
if (err) {
return res.status(500).send('Błąd pobierania zadań');
}
res.json(results);
});
};
exports.createTask = (req, res) => {
const { title, description } = req.body;
db.query('INSERT INTO tasks (title, description) VALUES (?, ?)', [title, description], (err) => {
if (err) {
return res.status(500).send('Błąd dodawania zadania');
}
res.status(201).send('Zadanie utworzone');
});
};
Te funkcje pozwalają na pobieranie wszystkich zadań oraz dodawanie nowego zadania. Błędy są obsługiwane z odpowiednimi kodami statusu.
Middleware do autoryzacji – authMiddleware.js
W katalogu app/middlewares/ mamy authMiddleware.js, który sprawdza, czy użytkownik jest autoryzowany:
const jwt = require('jsonwebtoken');
exports.verifyToken = (req, res, next) => {
const token = req.header('Authorization');
if (!token) {
return res.status(401).send('Brak tokena, autoryzacja nieudana');
}
try {
const decoded = jwt.verify(token, process.env.APP_JWT_SECRET);
req.user = decoded;
next();
} catch (err) {
res.status(400).send('Nieprawidłowy token');
}
};
Middleware odczytuje token JWT z nagłówka Authorization i weryfikuje jego poprawność za pomocą klucza tajnego (APP_JWT_SECRET).
Trasy – tasksRoutes.js
Plik tasksRoutes.js w katalogu app/routes/ definiuje trasy związane z zadaniami:
const express = require('express');
const router = express.Router();
const tasksController = require('../controllers/tasksController');
const authMiddleware = require('../middlewares/authMiddleware');
router.get('/', authMiddleware.verifyToken, tasksController.getAllTasks);
router.post('/', authMiddleware.verifyToken, tasksController.createTask);
module.exports = router;
Każda trasa wymaga weryfikacji tokenu JWT za pomocą middleware przed wykonaniem akcji na zadaniach.
index.js – Główna konfiguracja serwera
W katalogu app/ znajduje się index.js, który uruchamia serwer:
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const tasksRoutes = require('./routes/tasksRoutes');
const db = require('./config/db');
const app = express();
const PORT = process.env.DOCKER_NODE_PORT || 3000;
app.use(bodyParser.json());
app.use('/tasks', tasksRoutes);
db.connect((err) => {
if (err) {
console.error('Błąd połączenia z bazą danych:', err);
} else {
console.log('Połączono z bazą danych.');
}
});
app.listen(PORT, () => {
console.log(`Serwer działa na porcie ${PORT}`);
});
Ten plik konfiguruje podstawowe elementy serwera, łączy się z bazą danych i ustawia trasy dla tasks.
Dockerizacja aplikacji
Twój projekt ma również konfigurację do uruchomienia w kontenerach Docker.
Dockerfile_NodeJs
Plik Dockerfile_NodeJs konfiguruje środowisko dla aplikacji NodeJS:
FROM node:12
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE ${DOCKER_NODE_PORT}
CMD ["npm", "run", "dev"]
Ten Dockerfile buduje środowisko z NodeJS w wersji 12, instaluje zależności, kopiuje kod aplikacji i uruchamia serwer.
Dockerfile_Mysql
Plik Dockerfile_Mysql konfiguruje bazę danych:
FROM mysql:5.7
ENV MYSQL_ROOT_PASSWORD=${DOCKER_MYSQL_ROOT}
ENV MYSQL_DATABASE=${DOCKER_MYSQL_NAME}
ENV MYSQL_USER=${DOCKER_MYSQL_USER}
ENV MYSQL_PASSWORD=${DOCKER_MYSQL_PASS}
COPY ./mysql/01-schema.sql /docker-entrypoint-initdb.d/
Kontener MySQL jest tworzony z gotowym schematem bazy danych (01-schema.sql), co ułatwia wstępne uruchomienie.
docker-compose.yaml
Plik docker-compose.yaml definiuje konfigurację dla obu kontenerów:
version: '3.8'
services:
node:
build:
context: .
dockerfile: ./docker/Dockerfile_NodeJs
ports:
- "${DOCKER_NODE_PORT}:3000"
env_file:
- .env
depends_on:
- mysql
mysql:
build:
context: .
dockerfile: ./docker/Dockerfile_Mysql
ports:
- "${DOCKER_MYSQL_PORT}:3306"
env_file:
- .env
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
Docker Compose uruchamia zarówno serwer NodeJS, jak i bazę MySQL, jednocześnie zapewniając, że kontenery są w odpowiedniej kolejności (depends_on).
Podsumowanie
W tym projekcie zintegrowaliśmy różne elementy ExpressJS oraz Docker, aby stworzyć pełną aplikację do zarządzania zadaniami:
- Modularność: Dzieląc kod na kontrolery, middleware i trasy, uzyskaliśmy łatwy w zarządzaniu projekt.
- Docker: Konteneryzacja aplikacji oraz bazy danych umożliwia łatwe uruchamianie w różnych środowiskach.
- Autoryzacja: Middleware JWT zapewnia, że dostęp do danych jest odpowiednio chroniony.
Wykorzystując te elementy, uzyskałeś gotową aplikację, którą można uruchomić w kontenerach, co znacząco ułatwia wdrożenie i testowanie w różnych środowiskach.