This commit is contained in:
magdo
2026-01-28 02:19:01 +01:00
parent 3866a62feb
commit df9ca6eaaa
9 changed files with 1997 additions and 0 deletions
+314
View File
@@ -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}
+329
View File
@@ -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}
+339
View File
@@ -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}
+354
View File
@@ -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}
+345
View File
@@ -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}
Binary file not shown.
+24
View File
@@ -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}
+286
View File
@@ -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}
+6
View File
@@ -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}