Tworzenie Projektu Końcowego w Express.js

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.