diff --git a/Backend_ppt/RestAPI/Architektura.tex b/Backend_ppt/RestAPI/Architektura.tex new file mode 100644 index 0000000..e1a017f --- /dev/null +++ b/Backend_ppt/RestAPI/Architektura.tex @@ -0,0 +1,314 @@ +% REST API architektúra + +\section{REST API Architektúra} + +\begin{frame}{Miért van szükség architektúrára?} + \begin{block}{A probléma: "Spagetti kód"} + Kezdő fejlesztők gyakran mindent egy helyre írnak: + \begin{itemize} + \item Adatbázis lekérdezések a route-okban + \item Üzleti logika a controller-ekben + \item Validáció szétszórva mindenhol + \end{itemize} + + \textbf{Eredmény:} Áttekinthetetlen, nehezen karbantartható kód + \end{block} + + \begin{exampleblock}{A megoldás: Rétegezett architektúra} + Minden funkciónak megvan a saját helye és felelőssége. + \end{exampleblock} +\end{frame} + +\begin{frame}{5-rétegű architektúra} + \begin{block}{Separation of Concerns - Felelősségek szétválasztása} + \begin{itemize} + \item \textbf{Karbantarthatóság} - Könnyebb módosítás, hibakeresés + \item \textbf{Tesztelhetőség} - Rétegek külön tesztelhetők + \item \textbf{Skálázhatóság (Scalability)} - Független fejlesztés és skálázás + \item \textbf{Újrafelhasználhatóság} - Rétegek több helyen használhatók + \end{itemize} + \end{block} + + \begin{alertblock}{Alapelv} + Minden réteg csak a saját felelősségével foglalkozik! + \end{alertblock} +\end{frame} + +\begin{frame}{Az 5 réteg} + \begin{enumerate} + \item \textbf{API Layer (Presentation Layer)} + \begin{itemize} + \item HTTP kérések fogadása + \item Route-ok, Controller-ek + \end{itemize} + + \item \textbf{Application Layer (Service Layer)} + \begin{itemize} + \item Üzleti logika koordinálása + \item Service-ek + \end{itemize} + + \item \textbf{Domain Layer (Business Logic)} + \begin{itemize} + \item Üzleti szabályok + \item Model-ek, Entity-k + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{Az 5 réteg (folyt.)} + \begin{enumerate} + \setcounter{enumi}{3} + \item \textbf{Infrastructure Layer} + \begin{itemize} + \item Adatbázis hozzáférés + \item Repository-k, ORM + \end{itemize} + + \item \textbf{Communication Layer} + \begin{itemize} + \item Adatok átalakítása + \item DTO-k, Validáció + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}[shrink=10]{Rétegek áttekintése - Diagram} + \begin{center} + \begin{tikzpicture}[node distance=0.3cm] + \node[draw, rectangle, fill=blue!20, minimum width=9cm, minimum height=1cm, align=center] (api) {\textbf{API réteg (API Layer)}\\Útvonalak, Vezérlők (Routes, Controllers)}; + + \node[draw, rectangle, fill=green!20, minimum width=9cm, minimum height=1cm, below=of api, align=center] (app) {\textbf{Alkalmazás réteg (Application Layer)}\\Szolgáltatások (Services)}; + + \node[draw, rectangle, fill=yellow!20, minimum width=9cm, minimum height=1cm, below=of app, align=center] (domain) {\textbf{Tartomány réteg (Domain Layer)}\\Modellek, Entitások (Models, Entities)}; + + \node[draw, rectangle, fill=red!20, minimum width=9cm, minimum height=1cm, below=of domain, align=center] (infra) {\textbf{Infrastruktúra réteg (Infrastructure Layer)}\\Tárolók, Adatbázis (Repository, DB)}; + + \node[draw, rectangle, fill=purple!20, minimum width=9cm, minimum height=1cm, below=of infra, align=center] (comm) {\textbf{Kommunikációs réteg (Communication Layer)}\\Adatátviteli objektumok, Ellenőrzés (DTOs, Validation)}; + + \draw[->, thick] (api.south) -- (app.north); + \draw[->, thick] (app.south) -- (domain.north); + \draw[->, thick] (domain.south) -- (infra.north); + \draw[<->, thick] (api.east) -- ++(1,0) |- (comm.east); + \end{tikzpicture} + \end{center} +\end{frame} + +\begin{frame}[fragile,shrink=15]{API Layer - Route és Controller} + \begin{exampleblock}{Route példa} + \tiny + \begin{verbatim} +// routes/user.routes.js +const express = require('express'); +const router = express.Router(); +const userController = require('../controllers/user.controller'); + +router.get('/', userController.getAllUsers); +router.get('/:id', userController.getUserById); +router.post('/', userController.createUser); + +module.exports = router; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{API Layer - Controller} + \begin{exampleblock}{Controller példa} + \tiny + \begin{verbatim} +// controllers/user.controller.js +const userService = require('../services/user.service'); + +exports.getAllUsers = async (req, res) => { + try { + const users = await userService.getAllUsers(); + res.json(users); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +exports.getUserById = async (req, res) => { + try { + const user = await userService.getUserById(req.params.id); + if (!user) return res.status(404).json({ error: 'Not found' }); + res.json(user); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Application Layer - Service} + \begin{exampleblock}{Service példa} + \tiny + \begin{verbatim} +// services/user.service.js +const userRepository = require('../repositories/user.repository'); + +exports.getAllUsers = async () => { + return await userRepository.findAll(); +}; + +exports.getUserById = async (id) => { + return await userRepository.findById(id); +}; + +exports.createUser = async (userData) => { + // Üzleti logika validálás + if (!userData.email || !userData.name) { + throw new Error('Email and name are required'); + } + return await userRepository.create(userData); +}; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Domain Layer - Model/Entity} + \begin{exampleblock}{User Model} + \tiny + \begin{verbatim} +// models/user.model.js +class User { + constructor(id, name, email, createdAt) { + this.id = id; + this.name = name; + this.email = email; + this.createdAt = createdAt || new Date(); + } + + // Üzleti logika metódusok + isActive() { + return this.active === true; + } + + hasRole(role) { + return this.roles.includes(role); + } +} + +module.exports = User; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Infrastructure Layer - Repository} + \begin{exampleblock}{Repository példa} + \tiny + \begin{verbatim} +// repositories/user.repository.js +const db = require('../config/database'); + +exports.findAll = async () => { + return await db.query('SELECT * FROM users'); +}; + +exports.findById = async (id) => { + const result = await db.query('SELECT * FROM users WHERE id = ?', [id]); + return result[0]; +}; + +exports.create = async (userData) => { + const result = await db.query( + 'INSERT INTO users (name, email) VALUES (?, ?)', + [userData.name, userData.email] + ); + return { id: result.insertId, ...userData }; +}; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Communication Layer - DTO} + \begin{exampleblock}{Data Transfer Object} + \tiny + \begin{verbatim} +// dtos/user.dto.js +class UserDTO { + constructor(user) { + this.id = user.id; + this.name = user.name; + this.email = user.email; + // Érzékeny mezők nem kerülnek át (pl. password) + } +} + +class CreateUserDTO { + constructor(data) { + this.name = data.name; + this.email = data.email; + this.password = data.password; + } + + validate() { + if (!this.email || !this.name || !this.password) { + throw new Error('All fields required'); + } + } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{Projekt struktúra - Mappa felépítés} + \begin{block}{Az 5-rétegű architektúra a mappákban} + \small + \begin{itemize} + \item \textbf{\texttt{src/}} - Forráskód gyökér + \item \quad \texttt{routes/} - \textcolor{blue}{API réteg}: útvonalak definiálása + \item \quad \texttt{controllers/} - \textcolor{blue}{API réteg}: HTTP kérések kezelése + \item \quad \texttt{services/} - \textcolor{green}{Alkalmazás réteg}: üzleti logika + \item \quad \texttt{models/} - \textcolor{orange}{Tartomány réteg}: adatmodellek + \item \quad \texttt{repositories/} - \textcolor{red}{Infrastruktúra réteg}: DB műveletek + \item \quad \texttt{dtos/} - \textcolor{purple}{Kommunikáció réteg}: adatátviteli objektumok + \item \quad \texttt{middlewares/} - Köztes réteg (autentikáció, naplózás) + \item \quad \texttt{config/} - Konfigurációs fájlok + \end{itemize} + \end{block} +\end{frame} + +\begin{frame}{Projekt struktúra - Függőségek} + \begin{block}{Kommunikáció a rétegek között} + \begin{itemize} + \item \textbf{Routes} $\rightarrow$ Controllers (útvonal hívja a vezérlőt) + \item \textbf{Controllers} $\rightarrow$ Services (vezérlő hívja a szolgáltatást) + \item \textbf{Services} $\rightarrow$ Models + Repositories (szolgáltatás használja az adatréteget) + \item \textbf{Repositories} $\rightarrow$ Database (tároló eléri az adatbázist) + \item \textbf{DTOs} $\leftrightarrow$ Minden réteg (minden réteg használhat DTO-kat) + \end{itemize} + \end{block} + + \begin{alertblock}{Fontos szabály} + A belső rétegek NEM függhetnek a külső rétegektől! A függőségek mindig befelé mutatnak. + \end{alertblock} +\end{frame} + +\begin{frame}{Clean Architecture} + \begin{block}{Dependency Rule} + Belső rétegek nem függhetnek külső rétegektől. A függőségek mindig befelé mutatnak. + \end{block} + + \begin{exampleblock}{Előnyök} + \begin{itemize} + \item Független a framework-től + \item Tesztelhető + \item Független az UI-tól + \item Független az adatbázistól + \item Független külső szolgáltatásoktól + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}{Összefoglalás - Architektúra} + \begin{itemize} + \item 5-rétegű architektúra a clean code érdekében + \item API Layer (API réteg): Route-ok és Controller-ek (útvonalak, vezérlők) + \item Application Layer (Alkalmazás réteg): Service-ek (szolgáltatások - üzleti logika) + \item Domain Layer (Tartomány réteg): Model-ek és Entity-k (modellek, entitások) + \item Infrastructure Layer (Infrastruktúra réteg): Repository-k és DB (tárolók, adatbázis) + \item Communication Layer (Kommunikációs réteg): DTO-k és validáció (adatátvitel, ellenőrzés) + \end{itemize} +\end{frame} diff --git a/Backend_ppt/RestAPI/CQRS.tex b/Backend_ppt/RestAPI/CQRS.tex new file mode 100644 index 0000000..9dd20cd --- /dev/null +++ b/Backend_ppt/RestAPI/CQRS.tex @@ -0,0 +1,329 @@ +% CQRS Pattern + +\section{CQRS Pattern} + +\begin{frame}{Mi a CQRS?} + \begin{block}{Command Query Responsibility Segregation} + \textbf{Magyarul:} Parancs-Lekérdezés Felelősség Szétválasztása + + \vspace{0.3cm} + + Az adatok \textbf{írásának} (módosítás) és \textbf{olvasásának} (lekérdezés) szétválasztása külön modellekre. + \end{block} + + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{exampleblock}{Command (Parancs)} + \textbf{Írási műveletek:} + \begin{itemize} + \item POST, PUT, PATCH, DELETE + \item Adatok módosítása + \item Validáció, üzleti logika + \item Pl: felhasználó létrehozása + \end{itemize} + \end{exampleblock} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{exampleblock}{Query (Lekérdezés)} + \textbf{Olvasási műveletek:} + \begin{itemize} + \item GET + \item Adatok lekérdezése + \item Optimalizált olvasás + \item Pl: felhasználók listázása + \end{itemize} + \end{exampleblock} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{Miért van szükség CQRS-re?} + \begin{block}{A probléma} + Hagyományos CRUD alkalmazásokban ugyanazt a modellt használjuk írásra és olvasásra. + Ez problémás lehet: + \begin{itemize} + \item Az írási és olvasási igények eltérőek + \item Az olvasás gyakran bonyolultabb (join-ok, aggregációk) + \item A teljesítmény-optimalizálás nehéz + \end{itemize} + \end{block} + + \begin{exampleblock}{A megoldás: CQRS} + Külön modellek külön optimalizálással: + \begin{itemize} + \item \textbf{Write Model} (Író modell): validáció, üzleti szabályok + \item \textbf{Read Model} (Olvasó modell): gyors lekérdezések, cache + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}{CQRS előnyei} + \begin{block}{Előnyök} + \begin{itemize} + \item \textbf{Független optimalizálás} - Az írás és olvasás külön optimalizálható + \begin{itemize} + \item Íráshoz: tranzakciók, validáció, konzisztencia + \item Olvasáshoz: cache, denormalizált adatok, gyors lekérdezések + \end{itemize} + + \item \textbf{Skálázhatóság (Scalability)} - Read és Write modellek külön skálázhatók + \begin{itemize} + \item Több olvasó szerver, kevesebb író szerver + \end{itemize} + + \item \textbf{Egyszerűség} - Komplexitás csökken + \begin{itemize} + \item Minden modell egy feladatra fókuszál + \end{itemize} + + \item \textbf{Teljesítmény (Performance)} - Gyorsabb lekérdezések + \end{itemize} + \end{block} +\end{frame} + +\begin{frame}{Mikor használjuk a CQRS-t?} + \begin{exampleblock}{Jó választás, ha:} + \begin{itemize} + \item \textbf{Komplex domain logika} van az alkalmazásban + \item \textbf{Nagy terhelés} várható (sok párhuzamos felhasználó) + \item \textbf{Eltérő írás/olvasás igények} + \begin{itemize} + \item Pl: ritkán írunk, sokszor olvasunk + \end{itemize} + \item \textbf{Különböző adatformátumok} kellenek íráshoz és olvasáshoz + \end{itemize} + \end{exampleblock} + + \begin{alertblock}{NEM használjuk, ha:} + \begin{itemize} + \item Egyszerű CRUD alkalmazás + \item Kis terhelés + \item Az írás és olvasás hasonló + \end{itemize} + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=10]{CRUD vs CQRS - Architektúra összehasonlítás} + \begin{center} + \begin{tikzpicture}[node distance=1.5cm] + % CRUD + \node[draw, rectangle, fill=blue!20, minimum width=2.5cm, minimum height=0.9cm, align=center] (crud) {\small CRUD\\Modell}; + \node[above=0.3cm of crud, font=\bfseries] {Hagyományos}; + \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of crud, align=center] (db1) {\small Adatbázis\\(Database)}; + \draw[<->, thick] (crud) -- (db1) node[midway, above, font=\tiny] {olvas/ír}; + + % CQRS + \node[draw, rectangle, fill=yellow!20, minimum width=2.5cm, minimum height=0.9cm, below=2cm of crud, align=center] (cmd) {\small Parancs\\(Command)}; + \node[draw, rectangle, fill=orange!20, minimum width=2.5cm, minimum height=0.9cm, below=0.6cm of cmd, align=center] (query) {\small Lekérdezés\\(Query)}; + \node[above=0.3cm of cmd, font=\bfseries] {CQRS}; + \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of cmd, align=center] (db2) {\small Író DB\\(Write)}; + \node[draw, rectangle, fill=green!20, minimum width=2.5cm, minimum height=0.9cm, right=1.2cm of query, align=center] (db3) {\small Olvasó DB\\(Read)}; + + \draw[->, thick] (cmd) -- (db2) node[midway, above, font=\tiny] {ír}; + \draw[<-, thick] (query) -- (db3) node[midway, above, font=\tiny] {olvas}; + \draw[->, dashed, thick] (db2) -- (db3) node[midway, right, font=\tiny] {szinkron}; + \end{tikzpicture} + \end{center} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Command - Írás példa} + \begin{exampleblock}{CreateUserCommand} + \tiny + \begin{verbatim} +// commands/create-user.command.js +class CreateUserCommand { + constructor(name, email, password) { + this.name = name; + this.email = email; + this.password = password; + } + + validate() { + if (!this.email || !this.name || !this.password) { + throw new Error('All fields required'); + } + if (this.password.length < 8) { + throw new Error('Password too short'); + } + } +} + +module.exports = CreateUserCommand; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Command Handler} + \begin{exampleblock}{CreateUserCommandHandler} + \tiny + \begin{verbatim} +// handlers/create-user.handler.js +const userRepository = require('../repositories/user.repository'); + +class CreateUserCommandHandler { + async handle(command) { + command.validate(); + + // Üzleti logika + const existingUser = await userRepository.findByEmail(command.email); + if (existingUser) { + throw new Error('Email already exists'); + } + + // Password hash + const hashedPassword = await bcrypt.hash(command.password, 10); + + // User létrehozás + const user = await userRepository.create({ + name: command.name, + email: command.email, + password: hashedPassword + }); + + return user; + } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Query - Olvasás példa} + \begin{exampleblock}{GetUsersQuery} + \tiny + \begin{verbatim} +// queries/get-users.query.js +class GetUsersQuery { + constructor(filters = {}) { + this.page = filters.page || 1; + this.limit = filters.limit || 10; + this.role = filters.role; + this.active = filters.active; + } +} + +// handlers/get-users.handler.js +class GetUsersQueryHandler { + async handle(query) { + // Optimalizált lekérdezés + const users = await userReadModel.find({ + role: query.role, + active: query.active, + skip: (query.page - 1) * query.limit, + limit: query.limit + }); + + return users; + } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{CQRS REST API-ban (1/2)} + \begin{exampleblock}{Command végpontok} + \tiny + \begin{verbatim} +// routes/user.routes.js +const CreateUserCommand = require('../commands/create-user.command'); +const createUserHandler = require('../handlers/create-user.handler'); + +router.post('/users', async (req, res) => { + try { + const command = new CreateUserCommand( + req.body.name, + req.body.email, + req.body.password + ); + const user = await createUserHandler.handle(command); + res.status(201).json(user); + } catch (error) { + res.status(400).json({ error: error.message }); + } +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{CQRS REST API-ban (2/2)} + \begin{exampleblock}{Query végpontok} + \tiny + \begin{verbatim} +const GetUsersQuery = require('../queries/get-users.query'); +const getUsersHandler = require('../handlers/get-users.handler'); + +router.get('/users', async (req, res) => { + try { + const query = new GetUsersQuery({ + page: req.query.page, + limit: req.query.limit, + role: req.query.role, + active: req.query.active + }); + const users = await getUsersHandler.handle(query); + res.json(users); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{CQRS előnyei és hátrányai} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{Előnyök} + \begin{itemize} + \item Jobb teljesítmény + \item Független skálázás + \item Egyszerűbb modellek + \item Optimalizált lekérdezések + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{alertblock}{Hátrányok} + \begin{itemize} + \item Komplexitás növekedés + \item Több kód + \item Szinkronizáció problémák + \item Eventual consistency + \end{itemize} + \end{alertblock} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}{Event Sourcing + CQRS} + \begin{block}{Event Sourcing} + Az állapotváltozások eseményekként tárolása, nem a végállapot mentése. + \end{block} + + \begin{exampleblock}{Események} + \begin{itemize} + \item UserCreatedEvent + \item UserEmailChangedEvent + \item UserDeactivatedEvent + \end{itemize} + \end{exampleblock} + + \begin{alertblock}{CQRS + ES kombinációja} + Command-ok eseményeket generálnak, Query modellek eseményekből épülnek fel. + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás - CQRS} + \begin{itemize} + \item CQRS = Command és Query szétválasztása + \item Command: adatok módosítása (POST, PUT, DELETE) + \item Query: adatok lekérdezése (GET) + \item Előnyök: teljesítmény, skálázhatóság + \item Hátrányok: komplexitás + \item Event Sourcing jól kombinálható CQRS-sel + \end{itemize} + + \vspace{0.5cm} + +\end{frame} diff --git a/Backend_ppt/RestAPI/Express_restapi.tex b/Backend_ppt/RestAPI/Express_restapi.tex new file mode 100644 index 0000000..6e74f06 --- /dev/null +++ b/Backend_ppt/RestAPI/Express_restapi.tex @@ -0,0 +1,339 @@ +% Express.js REST API + +\section{Express.js REST API} + +\begin{frame}{Mi az Express.js?} + \begin{block}{Express} + Gyors, minimalista web framework Node.js-hez. + \end{block} + + \begin{exampleblock}{Jellemzők} + \begin{itemize} + \item Egyszerű API + \item Middleware-alapú + \item Routing támogatás + \item Template engine-ek + \item Nagy közösség és sok plugin + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Express telepítése} + \begin{exampleblock}{NPM inicializálás és telepítés} + \tiny + \begin{verbatim} +npm init -y +npm install express + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{package.json} + \tiny + \begin{verbatim} +{ + "name": "my-api", + "version": "1.0.0", + "main": "server.js", + "scripts": { + "start": "node server.js", + "dev": "nodemon server.js" + }, + "dependencies": { + "express": "^4.18.2" + } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Első Express szerver} + \begin{exampleblock}{server.js} + \tiny + \begin{verbatim} +const express = require('express'); +const app = express(); +const PORT = 3000; + +// Middleware +app.use(express.json()); + +// Egyszerű route +app.get('/', (req, res) => { + res.json({ message: 'Hello, Express!' }); +}); + +// Szerver indítása +app.listen(PORT, () => { + console.log(`Server running on http://localhost:${PORT}`); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{GET végpont} + \begin{exampleblock}{Összes elem lekérdezése} + \tiny + \begin{verbatim} +let users = [ + {id: 1, name: 'Alice', email: 'alice@example.com'}, + {id: 2, name: 'Bob', email: 'bob@example.com'} +]; + +app.get('/api/users', (req, res) => { + res.json(users); +}); + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Egy elem lekérdezése} + \tiny + \begin{verbatim} +app.get('/api/users/:id', (req, res) => { + const user = users.find(u => u.id === parseInt(req.params.id)); + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + res.json(user); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{POST végpont} + \begin{exampleblock}{Új elem létrehozása} + \tiny + \begin{verbatim} +app.post('/api/users', (req, res) => { + const { name, email } = req.body; + + if (!name || !email) { + return res.status(400).json({ error: 'Name and email required' }); + } + + const newUser = { + id: users.length + 1, + name, + email + }; + + users.push(newUser); + res.status(201).json(newUser); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{PUT és PATCH végpontok} + \begin{exampleblock}{PUT - Teljes frissítés} + \tiny + \begin{verbatim} +app.put('/api/users/:id', (req, res) => { + const idx = users.findIndex(u => u.id === parseInt(req.params.id)); + if (idx === -1) return res.status(404).json({ error: 'Not found' }); + + users[idx] = { id: parseInt(req.params.id), ...req.body }; + res.json(users[idx]); +}); + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{PATCH - Részleges frissítés} + \tiny + \begin{verbatim} +app.patch('/api/users/:id', (req, res) => { + const user = users.find(u => u.id === parseInt(req.params.id)); + if (!user) return res.status(404).json({ error: 'Not found' }); + + Object.assign(user, req.body); + res.json(user); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{DELETE végpont} + \begin{exampleblock}{Elem törlése} + \tiny + \begin{verbatim} +app.delete('/api/users/:id', (req, res) => { + const idx = users.findIndex(u => u.id === parseInt(req.params.id)); + + if (idx === -1) { + return res.status(404).json({ error: 'User not found' }); + } + + users.splice(idx, 1); + res.status(204).send(); +}); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{204 No Content} + Sikeres törlés esetén nem kell response body. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Middleware-ek} + \begin{block}{Mi a Middleware?} + Függvények, amelyek hozzáférnek a request és response objektumokhoz, és a \texttt{next()} függvényhez. + \end{block} + + \begin{exampleblock}{Logger middleware} + \tiny + \begin{verbatim} +app.use((req, res, next) => { + console.log(`${req.method} ${req.url}`); + next(); +}); + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Auth middleware} + \tiny + \begin{verbatim} +const authMiddleware = (req, res, next) => { + const token = req.headers.authorization; + if (!token) return res.status(401).json({ error: 'Unauthorized' }); + next(); +}; + +app.get('/api/protected', authMiddleware, (req, res) => { + res.json({ data: 'Protected data' }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Error Handling} + \begin{exampleblock}{Error handling middleware} + \tiny + \begin{verbatim} +app.use((err, req, res, next) => { + console.error(err.stack); + res.status(500).json({ + error: { + message: err.message, + code: 'INTERNAL_ERROR' + } + }); +}); + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Try-catch async} + \tiny + \begin{verbatim} +app.get('/api/users/:id', async (req, res, next) => { + try { + const user = await getUserById(req.params.id); + if (!user) throw new Error('User not found'); + res.json(user); + } catch (error) { + next(error); + } +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Validáció Joi-val (1/2)} + \begin{exampleblock}{Joi telepítése} + \tiny + \begin{verbatim} +npm install joi + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Schema definiálás} + \tiny + \begin{verbatim} +const Joi = require('joi'); + +const userSchema = Joi.object({ + name: Joi.string().min(3).max(50).required(), + email: Joi.string().email().required(), + age: Joi.number().integer().min(0).max(120) +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Validáció Joi-val (2/2)} + \begin{exampleblock}{Validáció használata} + \tiny + \begin{verbatim} +app.post('/api/users', (req, res) => { + const { error, value } = userSchema.validate(req.body); + + if (error) { + return res.status(400).json({ + error: error.details[0].message + }); + } + + // value a validált adat + const newUser = { id: users.length + 1, ...value }; + users.push(newUser); + res.status(201).json(newUser); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Express Router - Moduláris routing (1/2)} + \begin{exampleblock}{routes/users.js} + \tiny + \begin{verbatim} +const express = require('express'); +const router = express.Router(); + +router.get('/', (req, res) => { + res.json({ message: 'Get all users' }); +}); + +router.get('/:id', (req, res) => { + res.json({ message: `Get user ${req.params.id}` }); +}); + +router.post('/', (req, res) => { + res.json({ message: 'Create user' }); +}); + +module.exports = router; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Express Router - Moduláris routing (2/2)} + \begin{exampleblock}{app.js - Router használata} + \tiny + \begin{verbatim} +const express = require('express'); +const app = express(); +const userRoutes = require('./routes/users'); + +app.use(express.json()); +app.use('/api/users', userRoutes); + +app.listen(3000); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Prefix} + Az \texttt{app.use('/api/users', userRoutes)} prefix-szel látja el az összes route-ot. + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás - Express} + \begin{itemize} + \item Express = minimalista web framework + \item Routing: GET, POST, PUT, PATCH, DELETE + \item Middleware-ek: kérések feldolgozása + \item Error handling: hibakezelő middleware + \item Validáció: Joi vagy más library + \item Router: moduláris útvonalkezelés + \end{itemize} + + \vspace{0.5cm} + +\end{frame} diff --git a/Backend_ppt/RestAPI/Postman.tex b/Backend_ppt/RestAPI/Postman.tex new file mode 100644 index 0000000..08e8d9e --- /dev/null +++ b/Backend_ppt/RestAPI/Postman.tex @@ -0,0 +1,354 @@ +% Postman + +\section{API tesztelés Postman-nel} + +\begin{frame}{Mi a Postman?} + \begin{block}{Postman} + API fejlesztéshez és teszteléshez használt platform. + \end{block} + + \begin{exampleblock}{Funkciók} + \begin{itemize} + \item HTTP kérések küldése + \item Collection-ök létrehozása + \item Environment változók + \item Automatikus tesztek + \item API dokumentáció generálás + \item Csapatmunka támogatás + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}{Postman telepítése} + \begin{enumerate} + \item \textbf{Weboldal}: \url{https://www.postman.com/downloads/} + \item \textbf{Letöltés}: Desktop app (Windows, Mac, Linux) + \item \textbf{Telepítés}: Installer futtatása + \item \textbf{Bejelentkezés}: Ingyenes fiók létrehozása (opcionális) + \end{enumerate} + + \vspace{0.5cm} + + \begin{alertblock}{Web verzió} + Használható böngészőben is, de a desktop app gyorsabb. + \end{alertblock} +\end{frame} + +\begin{frame}{Első kérés Postman-ben} + \begin{enumerate} + \item \textbf{New} $\rightarrow$ \textbf{HTTP Request} + \item \textbf{Metódus} kiválasztása: GET, POST, PUT, DELETE + \item \textbf{URL} megadása: \texttt{http://localhost:3000/api/users} + \item \textbf{Send} gomb + \item \textbf{Response} megjelenik alul + \end{enumerate} + + \vspace{0.3cm} + + \begin{exampleblock}{Példa} + GET \texttt{http://localhost:3000/api/users} + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=10]{HTTP metódusok Postman-ben} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{GET kérés} + \begin{itemize} + \item Metódus: GET + \item URL: /api/users + \item Body nem kell + \end{itemize} + \end{block} + + \begin{block}{POST kérés} + \begin{itemize} + \item Metódus: POST + \item URL: /api/users + \item Body tab $\rightarrow$ JSON + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{PUT kérés} + \begin{itemize} + \item Metódus: PUT + \item URL: /api/users/:id + \item Body tab $\rightarrow$ JSON + \end{itemize} + \end{block} + + \begin{block}{DELETE kérés} + \begin{itemize} + \item Metódus: DELETE + \item URL: /api/users/:id + \item Body nem kell + \end{itemize} + \end{block} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[shrink=10]{Request Body típusok} + \begin{enumerate} + \item \textbf{none} - Nincs body (GET, DELETE) + + \item \textbf{form-data} - Multipart form (fájl feltöltés) + + \item \textbf{x-www-form-urlencoded} - URL encoded form + + \item \textbf{raw} - Nyers adat + \begin{itemize} + \item JSON (leggyakoribb) + \item Text, XML, HTML + \end{itemize} + + \item \textbf{binary} - Fájl feltöltés + + \item \textbf{GraphQL} - GraphQL query + \end{enumerate} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Headers beállítása} + \begin{exampleblock}{Headers tab} + \tiny + \begin{verbatim} +Content-Type: application/json +Authorization: Bearer YOUR_TOKEN_HERE +Accept: application/json + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Fontos header-ek} + \begin{itemize} + \item \textbf{Content-Type} - Body formátuma + \item \textbf{Authorization} - Autentikáció + \item \textbf{Accept} - Milyen formátumot várunk + \end{itemize} + \end{alertblock} +\end{frame} + +\begin{frame}{Collection-ök} + \begin{block}{Mi a Collection?} + Kapcsolódó API kérések csoportosítása egy helyen. + \end{block} + + \begin{exampleblock}{Collection struktúra} + \begin{itemize} + \item \textbf{Users} + \begin{itemize} + \item GET All Users + \item GET User by ID + \item POST Create User + \item PUT Update User + \item DELETE User + \end{itemize} + \item \textbf{Products} + \item \textbf{Authentication} + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}{Collection létrehozása} + \begin{enumerate} + \item \textbf{Collections} $\rightarrow$ \textbf{+} (New Collection) + \item \textbf{Név}: pl. "My REST API" + \item \textbf{Add a request} $\rightarrow$ kérések hozzáadása + \item \textbf{Mappa}: Kérések mappákba szervezése + \item \textbf{Save} - Minden kérést menteni kell + \end{enumerate} + + \vspace{0.3cm} + + \begin{alertblock}{Előnyök} + Gyors hozzáférés, újrafelhasználás, megosztás csapattal. + \end{alertblock} +\end{frame} + +\begin{frame}[shrink=10]{Environment Variables} + \begin{block}{Miért?} + Különböző környezetek (dev, staging, prod) közötti váltás anélkül, hogy minden kérést módosítanánk. + \end{block} + + \begin{exampleblock}{Environment létrehozása} + \begin{enumerate} + \item Environments $\rightarrow$ + New Environment + \item Név: \texttt{Development} + \item Változók: + \begin{itemize} + \item \texttt{baseUrl}: \texttt{http://localhost:3000} + \item \texttt{apiKey}: \texttt{dev-key-123} + \item \texttt{token}: \texttt{eyJhbGc...} + \end{itemize} + \item Save + \end{enumerate} + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=10]{Environment használata} + \begin{exampleblock}{Változó használat} + \begin{itemize} + \item URL: \texttt{\{\{baseUrl\}\}/api/users} + \item Header: \texttt{Authorization: Bearer \{\{token\}\}} + \item Body: \texttt{\{"userId": "\{\{userId\}\}"\}} + \end{itemize} + \end{exampleblock} + + \begin{exampleblock}{Váltás környezetek között} + Jobb felső sarok $\rightarrow$ Environment kiválasztása + \begin{itemize} + \item Development + \item Staging + \item Production + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Tests (Tesztek) (1/2)} + \begin{block}{Mi a Test?} + JavaScript kód, amely a válasz után fut és ellenőrzi azt. + \end{block} + + \begin{exampleblock}{Tests tab - Alapvető tesztek} + \tiny + \begin{verbatim} +// Status code +pm.test("Status code is 200", function () { + pm.response.to.have.status(200); +}); + +// Response time +pm.test("Response time < 200ms", function () { + pm.expect(pm.response.responseTime).to.be.below(200); +}); + +// JSON response +pm.test("Response is JSON", function () { + pm.response.to.be.json; +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Tests (Tesztek) (2/2)} + \begin{exampleblock}{JSON response ellenőrzés} + \tiny + \begin{verbatim} +const response = pm.response.json(); + +pm.test("Name is John", function () { + pm.expect(response.name).to.eql("John Doe"); +}); + +pm.test("Has email field", function () { + pm.expect(response).to.have.property("email"); +}); + +pm.test("Email is valid", function () { + pm.expect(response.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/); +}); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Test Results} + Send után megjelenik a Tests panel az eredménnyel. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Pre-request Scripts} + \begin{block}{Mi az?} + JavaScript kód, amely a kérés előtt fut le. + \end{block} + + \begin{exampleblock}{Példák} + \tiny + \begin{verbatim} +// Timestamp generálás +pm.environment.set("timestamp", Date.now()); + +// Random szám +pm.environment.set("randomNumber", Math.floor(Math.random() * 1000)); + +// UUID generálás +pm.environment.set("uuid", pm.variables.replaceIn('{{$guid}}')); + +// Változó beállítása +pm.environment.set("userId", 123); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Változók mentése Tests-ből} + \begin{exampleblock}{Response-ból változó mentése} + \tiny + \begin{verbatim} +const response = pm.response.json(); + +// Token mentése +pm.environment.set("token", response.token); + +// User ID mentése +pm.environment.set("userId", response.id); + +// Használat következő kérésben +// URL: {{baseUrl}}/api/users/{{userId}} +// Header: Authorization: Bearer {{token}} + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Workflow} + Login $\rightarrow$ token mentése $\rightarrow$ Protected endpoint hívás + \end{alertblock} +\end{frame} + +\begin{frame}{Collection Runner} + \begin{block}{Mi az?} + Collection-ök automatikus futtatása sorban. + \end{block} + + \begin{exampleblock}{Használat} + \begin{enumerate} + \item Collection $\rightarrow$ Run + \item Kérések sorrendje beállítása + \item Iterations: hányszor fusson + \item Delay: várakozás kérések között + \item Run Collection + \end{enumerate} + \end{exampleblock} + + \begin{alertblock}{Előny} + Teljes API tesztelése egy gombnyomással. + \end{alertblock} +\end{frame} + +\begin{frame}{Dokumentáció generálás} + \begin{block}{Automatic documentation} + Postman automatikusan generál dokumentációt Collection-ből. + \end{block} + + \begin{exampleblock}{Lépések} + \begin{enumerate} + \item Collection $\rightarrow$ View Documentation + \item Publish documentation (opcionális) + \item Megosztás csapattal vagy publikusan + \end{enumerate} + \end{exampleblock} + + \begin{alertblock}{Előny} + Mindig naprakész dokumentáció a Collection alapján. + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás - Postman} + \begin{itemize} + \item Postman = API fejlesztési platform + \item HTTP kérések küldése és tesztelése + \item Collection-ök: kérések csoportosítása + \item Environment: változók különböző környezetekhez + \item Tests: automatikus válasz ellenőrzés + \item Pre-request Scripts: kérés előtti logika + \item Collection Runner: teljes API tesztelés + \item Dokumentáció: automatikus generálás + \end{itemize} +\end{frame} diff --git a/Backend_ppt/RestAPI/Routing.tex b/Backend_ppt/RestAPI/Routing.tex new file mode 100644 index 0000000..ed0e405 --- /dev/null +++ b/Backend_ppt/RestAPI/Routing.tex @@ -0,0 +1,345 @@ +% Routing + +\section{Routing} + +\begin{frame}{Mi a Routing?} + \begin{block}{Routing} + A kérések irányítása a megfelelő végpontokra (endpoint). + \end{block} + + \begin{exampleblock}{Route komponensek} + \begin{itemize} + \item \textbf{HTTP metódus} - GET, POST, PUT, DELETE + \item \textbf{URL path} - /api/users, /api/products/:id + \item \textbf{Handler függvény} - (req, res) => \{\} + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Alapvető routing} + \begin{exampleblock}{Egyszerű route-ok} + \tiny + \begin{verbatim} +app.get('/api/users', (req, res) => { + res.json({ message: 'Get all users' }); +}); + +app.post('/api/users', (req, res) => { + res.json({ message: 'Create user' }); +}); + +app.put('/api/users/:id', (req, res) => { + res.json({ message: `Update user ${req.params.id}` }); +}); + +app.delete('/api/users/:id', (req, res) => { + res.json({ message: `Delete user ${req.params.id}` }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Route paraméterek} + \begin{exampleblock}{:id paraméter} + \tiny + \begin{verbatim} +app.get('/api/users/:id', (req, res) => { + const userId = req.params.id; + res.json({ message: `User ID: ${userId}` }); +}); + +// Több paraméter +app.get('/api/users/:userId/posts/:postId', (req, res) => { + const { userId, postId } = req.params; + res.json({ userId, postId }); +}); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{req.params} + URL-ben megadott dinamikus értékek. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Query paraméterek} + \begin{exampleblock}{Query string} + \tiny + \begin{verbatim} +// GET /api/users?page=1&limit=10&role=admin + +app.get('/api/users', (req, res) => { + const { page, limit, role } = req.query; + + res.json({ + page: page || 1, + limit: limit || 10, + role: role + }); +}); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{req.query} + URL query string paraméterek (?key=value). + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Query - Szűrés, Rendezés, Lapozás} + \begin{exampleblock}{GET /api/users?page=1\&limit=10\&sort=name\&role=admin} + \tiny + \begin{verbatim} +app.get('/api/users', (req, res) => { + let { page = 1, limit = 10, sort = 'id', role } = req.query; + + page = parseInt(page); + limit = parseInt(limit); + + let filteredUsers = users; + if (role) { + filteredUsers = filteredUsers.filter(u => u.role === role); + } + + // Rendezés + filteredUsers.sort((a, b) => a[sort] > b[sort] ? 1 : -1); + + // Lapozás + const start = (page - 1) * limit; + const paginatedUsers = filteredUsers.slice(start, start + limit); + + res.json({ data: paginatedUsers, total: filteredUsers.length }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Express Router létrehozása} + \begin{exampleblock}{routes/users.js} + \tiny + \begin{verbatim} +const express = require('express'); +const router = express.Router(); + +router.get('/', (req, res) => { + res.json({ message: 'Get all users' }); +}); + +router.get('/:id', (req, res) => { + res.json({ message: `Get user ${req.params.id}` }); +}); + +router.post('/', (req, res) => { + res.json({ message: 'Create user' }); +}); + +router.put('/:id', (req, res) => { + res.json({ message: `Update user ${req.params.id}` }); +}); + +module.exports = router; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Router importálása} + \begin{exampleblock}{app.js} + \tiny + \begin{verbatim} +const express = require('express'); +const app = express(); + +const userRoutes = require('./routes/users'); +const productRoutes = require('./routes/products'); + +app.use(express.json()); +app.use('/api/users', userRoutes); +app.use('/api/products', productRoutes); + +app.listen(3000); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Prefix} + Az \texttt{app.use('/api/users', userRoutes)} minden route-hoz hozzáadja a prefix-et. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Nested (beágyazott) route-ok} + \begin{exampleblock}{Hierarchikus erőforrások} + \tiny + \begin{verbatim} +// GET /api/users/:userId/posts +router.get('/:userId/posts', (req, res) => { + const { userId } = req.params; + res.json({ message: `Posts for user ${userId}` }); +}); + +// GET /api/users/:userId/posts/:postId +router.get('/:userId/posts/:postId', (req, res) => { + const { userId, postId } = req.params; + res.json({ message: `User ${userId}, Post ${postId}` }); +}); + +// POST /api/users/:userId/posts +router.post('/:userId/posts', (req, res) => { + const { userId } = req.params; + res.json({ message: `Create post for user ${userId}` }); +}); + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{Controller-ek használata (1/2)} + \begin{block}{Separation of Concerns} + Route-ok csak az útvonalat definiálják, Controller-ek tartalmazzák a logikát. + \end{block} + + \begin{exampleblock}{controllers/user.controller.js} + \tiny + \begin{verbatim} +exports.getAllUsers = async (req, res) => { + try { + const users = await User.find(); + res.json(users); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + +exports.getUserById = async (req, res) => { + try { + const user = await User.findById(req.params.id); + if (!user) return res.status(404).json({ error: 'Not found' }); + res.json(user); + } catch (error) { + res.status(500).json({ error: error.message }); + } +}; + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Controller-ek használata (2/2)} + \begin{exampleblock}{routes/users.js} + \tiny + \begin{verbatim} +const express = require('express'); +const router = express.Router(); +const userController = require('../controllers/user.controller'); + +router.get('/', userController.getAllUsers); +router.get('/:id', userController.getUserById); +router.post('/', userController.createUser); +router.put('/:id', userController.updateUser); +router.delete('/:id', userController.deleteUser); + +module.exports = router; + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Tiszta kód} + Route fájlok rövidek, Controller-ekben a logika. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Middleware route-okban} + \begin{exampleblock}{Auth middleware route-ra} + \tiny + \begin{verbatim} +const auth = require('../middlewares/auth'); + +router.get('/protected', auth, (req, res) => { + res.json({ message: 'Protected data' }); +}); + +router.post('/users', auth, validateUser, userController.createUser); + +// Minden route-ra +router.use('/admin', auth, adminRoutes); + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Több middleware} + Több middleware függvényt is alkalmazhatsz sorban. + \end{alertblock} +\end{frame} + +\begin{frame}[fragile,shrink=15]{API verziókezelés (1/2)} + \begin{exampleblock}{Verziókezelés URL-ben} + \tiny + \begin{verbatim} +// V1 routes +const v1Routes = require('./routes/v1'); +app.use('/api/v1', v1Routes); + +// V2 routes +const v2Routes = require('./routes/v2'); +app.use('/api/v2', v2Routes); + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Struktúra} + \tiny + \begin{itemize} + \item routes/v1/index.js + \item routes/v1/users.js + \item routes/v1/products.js + \item routes/v2/index.js + \item routes/v2/users.js + \end{itemize} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{API verziókezelés (2/2)} + \begin{exampleblock}{routes/v1/index.js} + \tiny + \begin{verbatim} +const express = require('express'); +const router = express.Router(); +const userRoutes = require('./users'); +const productRoutes = require('./products'); + +router.use('/users', userRoutes); +router.use('/products', productRoutes); + +module.exports = router; + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Használat} + \tiny + \begin{verbatim} +GET /api/v1/users +GET /api/v2/users + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[shrink=10]{Route Best Practices} + \begin{alertblock}{Konvenciók} + \begin{enumerate} + \item Használj \textbf{főneveket}: \texttt{/users}, \texttt{/products} + \item Használj \textbf{többes számot}: \texttt{/users} $\checkmark$ + \item \textbf{Hierarchia}: \texttt{/users/:userId/posts/:postId} + \item \textbf{Kisbetűs URL}: \texttt{/api/users} $\checkmark$ + \item \textbf{Kötőjel}: \texttt{/user-profiles} $\checkmark$ + \item \textbf{Verziókezelés}: \texttt{/api/v1/users} + \item \textbf{Szűrés query-vel}: \texttt{/users?role=admin} + \item \textbf{Ne igék}: \texttt{POST /users} $\checkmark$, \texttt{/createUser} $\times$ + \end{enumerate} + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás - Routing} + \begin{itemize} + \item Routing = kérések irányítása végpontokra + \item Route paraméterek: \texttt{/users/:id} $\rightarrow$ \texttt{req.params.id} + \item Query paraméterek: \texttt{/users?page=1} $\rightarrow$ \texttt{req.query.page} + \item Express Router: moduláris route szervezés + \item Controller-ek: logika szétválasztása + \item Middleware: route-specifikus middleware-ek + \item Verziókezelés: /api/v1, /api/v2 + \end{itemize} + + \vspace{0.5cm} + +\end{frame} diff --git a/Backend_ppt/RestAPI/restapi.pdf b/Backend_ppt/RestAPI/restapi.pdf new file mode 100644 index 0000000..6e8c569 Binary files /dev/null and b/Backend_ppt/RestAPI/restapi.pdf differ diff --git a/Backend_ppt/RestAPI/restapi.tex b/Backend_ppt/RestAPI/restapi.tex new file mode 100644 index 0000000..8fef865 --- /dev/null +++ b/Backend_ppt/RestAPI/restapi.tex @@ -0,0 +1,24 @@ +\documentclass[usenames,dvipsnames,aspectratio=169]{beamer} +\usepackage{../common/webfejl} + +% Automatikus frame törés engedélyezése túl hosszú tartalomnál +\setbeamertemplate{frametitle continuation}[from second][\insertcontinuationcountroman] + +\title[Webtechnológia és webalkalmazás-fejlesztés - Rest API]{Webtechnológia és webalkalmazás-fejlesztés - Rest API} +\subtitle{Rest API} + +\begin{document} + +\begin{frame}[plain] + \titlepage + \logoalul +\end{frame} + +\input{restapi_alapok.tex} +\input{Architektura.tex} +\input{CQRS.tex} +\input{Express_restapi.tex} +\input{Routing.tex} +\input{Postman.tex} + +\end{document} \ No newline at end of file diff --git a/Backend_ppt/RestAPI/restapi_alapok.tex b/Backend_ppt/RestAPI/restapi_alapok.tex new file mode 100644 index 0000000..26d9064 --- /dev/null +++ b/Backend_ppt/RestAPI/restapi_alapok.tex @@ -0,0 +1,286 @@ +% REST API alapok + +\section{REST API Alapok} + +\begin{frame}{Mi a REST API?} + \begin{block}{REST - Representational State Transfer} + \begin{itemize} + \item Architektúra stílus webes szolgáltatásokhoz + \item Roy Fielding dolgozta ki 2000-ben + \item HTTP protokollon alapuló kommunikáció + \item Állapotmentes (stateless) kliens-szerver kapcsolat + \end{itemize} + \end{block} + + \begin{exampleblock}{API - Application Programming Interface} + Interfész, amin keresztül különböző alkalmazások kommunikálnak egymással. + \end{exampleblock} +\end{frame} + +\begin{frame}{REST alapelvek} + \begin{enumerate} + \item \textbf{Kliens-szerver architektúra} + \begin{itemize} + \item Szétválasztott felelősségek + \item Kliens: felhasználói felület + \item Szerver: adatkezelés, üzleti logika + \end{itemize} + + \item \textbf{Állapotmentesség (Stateless)} + \begin{itemize} + \item Minden kérés független + \item Szerver nem tárol kliens állapotot + \end{itemize} + + \item \textbf{Cachelhető} + \begin{itemize} + \item Válaszok cache-elhetők a teljesítmény növelésére + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{REST alapelvek (folyt.)} + \begin{enumerate} + \setcounter{enumi}{3} + \item \textbf{Egységes interfész} + \begin{itemize} + \item Erőforrások URI-kon keresztül azonosíthatók + \item Reprezentációkon keresztüli manipuláció + \item Önleíró üzenetek + \end{itemize} + + \item \textbf{Rétegzett rendszer} + \begin{itemize} + \item Köztes rétegek (proxy, gateway) használhatók + \item Kliens nem tudja, közvetlenül a szerverhez kapcsolódik-e + \end{itemize} + \end{enumerate} +\end{frame} + +\begin{frame}{Erőforrások és URI-k} + \begin{block}{Erőforrás (Resource)} + Minden, amit azonosítani és kezelni akarunk (pl. felhasználó, termék, rendelés). + \end{block} + + \begin{exampleblock}{URI példák} + \begin{itemize} + \item \texttt{/users} - Összes felhasználó + \item \texttt{/users/123} - Adott felhasználó (ID: 123) + \item \texttt{/users/123/posts} - Egy felhasználó posztjai + \item \texttt{/products} - Összes termék + \item \texttt{/products/456} - Adott termék + \end{itemize} + \end{exampleblock} + + \begin{alertblock}{Konvenció} + Használj \textbf{főneveket} és \textbf{többes számot}: \texttt{/users}, \texttt{/products} + \end{alertblock} +\end{frame} + +\begin{frame}{HTTP metódusok} + \begin{table} + \small + \begin{tabular}{|l|l|l|l|} + \hline + \textbf{Metódus} & \textbf{Művelet} & \textbf{Idemp.} & \textbf{Példa} \\ + \hline + GET & Lekérdezés & Igen & Lista/részlet \\ + POST & Létrehozás & Nem & Új elem \\ + PUT & Frissítés (teljes) & Igen & Teljes csere \\ + PATCH & Frissítés (rész) & Nem & Rész módosítás \\ + DELETE & Törlés & Igen & Elem törlése \\ + \hline + \end{tabular} + \end{table} + + \vspace{0.3cm} + + \begin{block}{Idempotencia} + Ugyanaz a kérés többször végrehajtva ugyanazt az eredményt adja. + \end{block} +\end{frame} + +\begin{frame}[fragile,shrink=10]{HTTP metódusok - GET példák} + \begin{exampleblock}{Összes elem lekérdezése} + \tiny + \begin{verbatim} +GET /api/users +Response: 200 OK +[ + {"id": 1, "name": "Alice", "email": "alice@example.com"}, + {"id": 2, "name": "Bob", "email": "bob@example.com"} +] + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{Egy elem lekérdezése} + \tiny + \begin{verbatim} +GET /api/users/1 +Response: 200 OK +{"id": 1, "name": "Alice", "email": "alice@example.com"} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{HTTP metódusok - POST, PUT} + \begin{exampleblock}{POST - Új elem létrehozása} + \tiny + \begin{verbatim} +POST /api/users +Body: {"name": "Charlie", "email": "charlie@example.com"} +Response: 201 Created +{"id": 3, "name": "Charlie", "email": "charlie@example.com"} + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{PUT - Teljes frissítés} + \tiny + \begin{verbatim} +PUT /api/users/1 +Body: {"name": "Alice Smith", "email": "alice.smith@example.com"} +Response: 200 OK +{"id": 1, "name": "Alice Smith", "email": "alice.smith@example.com"} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{HTTP metódusok - PATCH, DELETE} + \begin{exampleblock}{PATCH - Részleges frissítés} + \tiny + \begin{verbatim} +PATCH /api/users/1 +Body: {"email": "newemail@example.com"} +Response: 200 OK +{"id": 1, "name": "Alice", "email": "newemail@example.com"} + \end{verbatim} + \end{exampleblock} + + \begin{exampleblock}{DELETE - Törlés} + \tiny + \begin{verbatim} +DELETE /api/users/1 +Response: 204 No Content + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{HTTP státuszkódok} + \begin{columns} + \begin{column}{0.48\textwidth} + \begin{block}{2xx - Sikeres} + \begin{itemize} + \item \textbf{200} OK + \item \textbf{201} Created + \item \textbf{204} No Content + \end{itemize} + \end{block} + + \begin{block}{4xx - Kliens hiba} + \begin{itemize} + \item \textbf{400} Bad Request + \item \textbf{401} Unauthorized + \item \textbf{403} Forbidden + \item \textbf{404} Not Found + \end{itemize} + \end{block} + \end{column} + + \begin{column}{0.48\textwidth} + \begin{block}{5xx - Szerver hiba} + \begin{itemize} + \item \textbf{500} Internal Error + \item \textbf{502} Bad Gateway + \item \textbf{503} Service Unavailable + \end{itemize} + \end{block} + + \begin{alertblock}{Fontos} + Mindig a megfelelő státuszkódot küldd! + \end{alertblock} + \end{column} + \end{columns} +\end{frame} + +\begin{frame}[fragile,shrink=10]{JSON formátum} + \begin{block}{JavaScript Object Notation} + A REST API-k leggyakoribb adatformátuma. + \end{block} + + \begin{exampleblock}{Példa} + \tiny + \begin{verbatim} +{ + "id": 1, + "name": "John Doe", + "email": "john@example.com", + "age": 30, + "active": true, + "roles": ["user", "admin"], + "address": { + "city": "Budapest", + "zip": "1111" + } +} + \end{verbatim} + \end{exampleblock} +\end{frame} + +\begin{frame}{REST vs SOAP} + \begin{table} + \tiny + \begin{tabular}{|l|p{4cm}|p{4cm}|} + \hline + & \textbf{REST} & \textbf{SOAP} \\ + \hline + Protokoll & HTTP & HTTP, SMTP, TCP \\ + Formátum & JSON, XML & Csak XML \\ + Állapot & Stateless & Lehet stateful \\ + Sebesség & Gyorsabb & Lassabb \\ + Használat & Egyszerűbb & Komplexebb \\ + \hline + \end{tabular} + \end{table} + + \vspace{0.3cm} + + \begin{exampleblock}{Mikor REST?} + Webes és mobil alkalmazások, publikus API-k, mikroszolgáltatások + \end{exampleblock} +\end{frame} + +\begin{frame}[fragile,shrink=10]{Hibakezelés} + \begin{exampleblock}{Hibaválasz struktúra} + \tiny + \begin{verbatim} +{ + "error": { + "code": "USER_NOT_FOUND", + "message": "A megadott felhasználó nem található", + "details": "User with ID 123 does not exist", + "timestamp": "2026-01-27T20:30:00Z" + } +} + \end{verbatim} + \end{exampleblock} + + \begin{alertblock}{Best Practices} + \begin{itemize} + \item Mindig küldd a megfelelő HTTP státuszkódot + \item Adj értelmes hibaüzeneteket + \item Ne adj ki érzékeny információkat + \end{itemize} + \end{alertblock} +\end{frame} + +\begin{frame}{Összefoglalás - REST Alapok} + \begin{itemize} + \item \textbf{REST} = Representational State Transfer (Reprezentációs Állapotátvitel) + \item Architektúra stílus HTTP-n keresztül + \item \textbf{Erőforrások} (Resources) URI-kon keresztül azonosíthatók + \item \textbf{HTTP metódusok}: GET (olvasás), POST (létrehozás), PUT/PATCH (módosítás), DELETE (törlés) + \item \textbf{Státuszkódok} (Status Codes) jelzik a kérés eredményét + \item \textbf{JSON} a leggyakoribb adatformátum + \item \textbf{Állapotmentes} (Stateless) kommunikáció + \end{itemize} +\end{frame} diff --git a/Backend_ppt/common/webfejl.sty b/Backend_ppt/common/webfejl.sty index bdb5acb..d847fd7 100644 --- a/Backend_ppt/common/webfejl.sty +++ b/Backend_ppt/common/webfejl.sty @@ -3,6 +3,12 @@ \usepackage[magyar]{babel} \usepackage{indentfirst} \usepackage{graphicx} +\usepackage{tikz} +\usetikzlibrary{positioning} +\usepackage{fancyvrb} + +% Globális verbatim beállítás - tiny betűméret minden verbatim blokkhoz +\fvset{fontsize=\tiny} \usepackage{listingsutf8} \usepackage{textcomp} \usepackage{eurosym}