database added
This commit is contained in:
@@ -0,0 +1,354 @@
|
|||||||
|
\section{Aggregációk}
|
||||||
|
|
||||||
|
\begin{frame}{Mi az aggregáció?}
|
||||||
|
\begin{block}{Aggregált lekérdezések}
|
||||||
|
Adatok összesítése és elemzése számítások segítségével
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Prisma aggregáció műveletek}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{count} - Rekordok számlálása
|
||||||
|
\item \texttt{avg} - Átlag számítás
|
||||||
|
\item \texttt{sum} - Összegzés
|
||||||
|
\item \texttt{min} - Minimum érték
|
||||||
|
\item \texttt{max} - Maximum érték
|
||||||
|
\item \texttt{groupBy} - Csoportosítás
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Példa séma}
|
||||||
|
\begin{block}{schema.prisma}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
model Product {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
price Float
|
||||||
|
stock Int
|
||||||
|
categoryId Int
|
||||||
|
category Category @relation(fields: [categoryId],
|
||||||
|
references: [id])
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
}
|
||||||
|
model Category {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
products Product[]
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Count - Számlálás}
|
||||||
|
\begin{block}{Összes rekord számolása}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const prisma = require('./prisma');
|
||||||
|
async function countProducts() {
|
||||||
|
const count = await prisma.product.count();
|
||||||
|
console.log(`Összes termék: ${count}`);
|
||||||
|
}
|
||||||
|
// Szűrt számlálás
|
||||||
|
async function countExpensiveProducts() {
|
||||||
|
const count = await prisma.product.count({
|
||||||
|
where: {
|
||||||
|
price: {
|
||||||
|
gt: 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`Drága termékek: ${count}`);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Aggregate - Alapok}
|
||||||
|
\begin{block}{Egyszerű aggregáció}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getProductStats() {
|
||||||
|
const stats = await prisma.product.aggregate({
|
||||||
|
_count: true,
|
||||||
|
_avg: {price: true},
|
||||||
|
|
||||||
|
_sum: {stock: true},
|
||||||
|
|
||||||
|
_min: {price: true},
|
||||||
|
|
||||||
|
_max: {price: true}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(stats);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Aggregate - Eredmény}
|
||||||
|
\begin{block}{Visszatérési érték példa}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
{
|
||||||
|
_count: 150,
|
||||||
|
_avg: {
|
||||||
|
price: 1250.50
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
stock: 5420
|
||||||
|
},
|
||||||
|
_min: {
|
||||||
|
price: 99.99
|
||||||
|
},
|
||||||
|
_max: {
|
||||||
|
price: 9999.99
|
||||||
|
}}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Aggregate - Szűréssel}
|
||||||
|
\begin{block}{Where feltétellel}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getCategoryStats(categoryId) {
|
||||||
|
const stats = await prisma.product.aggregate({
|
||||||
|
where: {
|
||||||
|
categoryId: categoryId
|
||||||
|
},
|
||||||
|
_count: true,
|
||||||
|
_avg: {
|
||||||
|
price: true
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
stock: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{GroupBy - Csoportosítás}
|
||||||
|
\begin{block}{Kategóriánkénti csoportosítás}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function groupByCategory() {
|
||||||
|
const result = await prisma.product.groupBy({
|
||||||
|
by: ['categoryId'],
|
||||||
|
_count: true,
|
||||||
|
_avg: {
|
||||||
|
price: true
|
||||||
|
},
|
||||||
|
_sum: {
|
||||||
|
stock: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{GroupBy - Eredmény}
|
||||||
|
\begin{block}{Csoportosított adatok}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
[
|
||||||
|
{
|
||||||
|
categoryId: 1,
|
||||||
|
_count: 45,
|
||||||
|
_avg: { price: 599.99 },
|
||||||
|
_sum: { stock: 1200 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
categoryId: 2,
|
||||||
|
_count: 67,
|
||||||
|
_avg: { price: 1299.50 },
|
||||||
|
_sum: { stock: 890 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{GroupBy - Having feltétel}
|
||||||
|
\begin{block}{Szűrés az aggregált eredményen}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getCategoriesWithHighAvgPrice() {
|
||||||
|
const result = await prisma.product.groupBy({
|
||||||
|
by: ['categoryId'],
|
||||||
|
_avg: {price: true},
|
||||||
|
_count: true,
|
||||||
|
having: {
|
||||||
|
price: {
|
||||||
|
_avg: {gt: 1000}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{GroupBy - Rendezés}
|
||||||
|
\begin{block}{OrderBy aggregált mezőn}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getCategoriesOrderedByAvgPrice() {
|
||||||
|
const result = await prisma.product.groupBy({
|
||||||
|
by: ['categoryId'],
|
||||||
|
_avg: {price: true},
|
||||||
|
_count: true,
|
||||||
|
orderBy: {
|
||||||
|
_avg: {price: 'desc'}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Több mező szerinti csoportosítás}
|
||||||
|
\begin{block}{Összetett GroupBy}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Bővített séma szükséges: inStock boolean mező
|
||||||
|
async function groupByCategoryAndStock() {
|
||||||
|
const result = await prisma.product.groupBy({
|
||||||
|
by: ['categoryId', 'inStock'],
|
||||||
|
_count: true,
|
||||||
|
_avg: {price: true},
|
||||||
|
orderBy: {categoryId: 'asc'}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Relációk és aggregáció}
|
||||||
|
\begin{block}{Kapcsolt táblák aggregálása}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getCategoriesWithProductCount() {
|
||||||
|
const categories = await prisma.category.findMany({
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: {
|
||||||
|
products: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Eredmény: [{ id: 1, name: 'Electronics',
|
||||||
|
// _count: { products: 45 } }, ...]
|
||||||
|
return categories;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Relációs szűrés aggregációval}
|
||||||
|
\begin{block}{Több mint N termékkel rendelkező kategóriák}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getCategoriesWithManyProducts() {
|
||||||
|
const categories = await prisma.category.findMany({
|
||||||
|
where: {
|
||||||
|
products: {some: {}}
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
_count: {
|
||||||
|
select: {products: true}
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
// Csak azok a kategóriák, amikhez van termék
|
||||||
|
return categories.filter(c => c._count.products > 10);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Express API - Statisztika endpoint}
|
||||||
|
\begin{block}{Aggregált adatok REST API-n keresztül}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const express = require('express');
|
||||||
|
const prisma = require('./prisma');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.get('/api/stats/products', async (req, res) => {
|
||||||
|
const stats = await prisma.product.aggregate({
|
||||||
|
_count: true,
|
||||||
|
_avg: { price: true },
|
||||||
|
_sum: { stock: true },
|
||||||
|
_min: { price: true },
|
||||||
|
_max: { price: true }
|
||||||
|
});
|
||||||
|
res.json(stats);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Express API - Csoportosított adatok}
|
||||||
|
\begin{block}{GroupBy endpoint}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
app.get('/api/stats/by-category', async (req, res) => {
|
||||||
|
const stats = await prisma.product.groupBy({
|
||||||
|
by: ['categoryId'],
|
||||||
|
_count: true,
|
||||||
|
_avg: { price: true },
|
||||||
|
_sum: { stock: true },
|
||||||
|
orderBy: {
|
||||||
|
_avg: {
|
||||||
|
price: 'desc'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
res.json(stats);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Egyedi aggregáció - Raw Query}
|
||||||
|
\begin{block}{SQL használata komplex esetekben}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function customAggregation() {
|
||||||
|
const result = await prisma.$queryRaw`
|
||||||
|
SELECT "categoryId",
|
||||||
|
COUNT(*) as count,
|
||||||
|
AVG(price) as avgPrice,
|
||||||
|
SUM(stock) as totalStock
|
||||||
|
FROM "Product"
|
||||||
|
WHERE price > 100
|
||||||
|
GROUP BY "categoryId"
|
||||||
|
HAVING AVG(price) > 500
|
||||||
|
ORDER BY avgPrice DESC
|
||||||
|
`;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Aggregáció - Best Practices}
|
||||||
|
\begin{block}{Ajánlások}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Használj \texttt{select} és \texttt{where} feltételeket a teljesítmény növelésére
|
||||||
|
\item GroupBy esetén mindig gondold át az indexelést
|
||||||
|
\item Nagy adathalmazoknál fontold meg a lapozást
|
||||||
|
\item Komplex esetekben raw SQL lehet hatékonyabb
|
||||||
|
\item Cacheld az aggregált eredményeket, ha ritkán változnak
|
||||||
|
\item Használj \texttt{having} feltételt az aggregált adatok szűrésére
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás}
|
||||||
|
\begin{block}{Prisma aggregációk}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{count} - Egyszerű és szűrt számlálás
|
||||||
|
\item \textbf{aggregate} - Átlag, összeg, min, max számítások
|
||||||
|
\item \textbf{groupBy} - Csoportosítás és aggregálás
|
||||||
|
\item \textbf{having} - Szűrés aggregált eredményen
|
||||||
|
\item \textbf{orderBy} - Rendezés aggregált mezők szerint
|
||||||
|
\item \textbf{\_count} - Kapcsolt rekordok számlálása
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
\section{CRUD műveletek}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{CRUD műveletek}
|
||||||
|
\begin{block}{Mi az a CRUD?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{C}reate - Létrehozás
|
||||||
|
\item \textbf{R}ead - Olvasás
|
||||||
|
\item \textbf{U}pdate - Módosítás
|
||||||
|
\item \textbf{D}elete - Törlés
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\vspace{0.5cm}
|
||||||
|
|
||||||
|
\begin{block}{Adatbázis műveletek}
|
||||||
|
Az alapvető adatbázis műveletek, amelyek minden adatkezelő rendszerben megtalálhatók.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Create - Létrehozás}
|
||||||
|
\begin{block}{SQL - INSERT}
|
||||||
|
\begin{lstlisting}[language=SQL]
|
||||||
|
INSERT INTO users (name, email, age)
|
||||||
|
VALUES ('John Doe', 'john@example.com', 30);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{REST API - POST}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
POST /api/users
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "John Doe",
|
||||||
|
"email": "john@example.com",
|
||||||
|
"age": 30
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - Olvasás}
|
||||||
|
\begin{block}{SQL - SELECT}
|
||||||
|
\begin{lstlisting}[language=SQL]
|
||||||
|
-- Összes rekord
|
||||||
|
SELECT * FROM users;
|
||||||
|
|
||||||
|
-- Egy adott rekord
|
||||||
|
SELECT * FROM users WHERE id = 1;
|
||||||
|
|
||||||
|
-- Szűrés és rendezés
|
||||||
|
SELECT name, email FROM users
|
||||||
|
WHERE age > 25
|
||||||
|
ORDER BY name ASC;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - REST API}
|
||||||
|
\begin{block}{REST API - GET}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Összes felhasználó
|
||||||
|
GET /api/users
|
||||||
|
|
||||||
|
// Egy adott felhasználó
|
||||||
|
GET /api/users/1
|
||||||
|
|
||||||
|
// Szűrés query paraméterekkel
|
||||||
|
GET /api/users?age=30&sort=name
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Update - Módosítás}
|
||||||
|
\begin{block}{SQL - UPDATE}
|
||||||
|
\begin{lstlisting}[language=SQL]
|
||||||
|
UPDATE users
|
||||||
|
SET email = 'newemail@example.com', age = 31
|
||||||
|
WHERE id = 1;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{REST API - PUT/PATCH}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Teljes frissítés
|
||||||
|
PUT /api/users/1
|
||||||
|
{ "name": "John Doe", "email": "new@example.com", "age": 31 }
|
||||||
|
|
||||||
|
// Részleges frissítés
|
||||||
|
PATCH /api/users/1
|
||||||
|
{ "email": "new@example.com" }
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Delete - Törlés}
|
||||||
|
\begin{block}{SQL - DELETE}
|
||||||
|
\begin{lstlisting}[language=SQL]
|
||||||
|
-- Egy adott rekord törlése
|
||||||
|
DELETE FROM users WHERE id = 1;
|
||||||
|
|
||||||
|
-- Minden rekord törlése (óvatosan!)
|
||||||
|
DELETE FROM users;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{REST API - DELETE}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
DELETE /api/users/1
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{CRUD vs HTTP metódusok}
|
||||||
|
\begin{table}
|
||||||
|
\begin{tabular}{|l|l|l|l|}
|
||||||
|
\hline
|
||||||
|
\textbf{CRUD} & \textbf{HTTP} & \textbf{SQL} & \textbf{Leírás} \\
|
||||||
|
\hline
|
||||||
|
Create & POST & INSERT & Új erőforrás létrehozása \\
|
||||||
|
\hline
|
||||||
|
Read & GET & SELECT & Erőforrás lekérdezése \\
|
||||||
|
\hline
|
||||||
|
Update & PUT/PATCH & UPDATE & Erőforrás módosítása \\
|
||||||
|
\hline
|
||||||
|
Delete & DELETE & DELETE & Erőforrás törlése \\
|
||||||
|
\hline
|
||||||
|
\end{tabular}
|
||||||
|
\end{table}
|
||||||
|
\end{frame}
|
||||||
Binary file not shown.
@@ -0,0 +1,26 @@
|
|||||||
|
\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{crud_operations.tex}
|
||||||
|
\input{orm.tex}
|
||||||
|
\input{prisma.tex}
|
||||||
|
\input{database_connection.tex}
|
||||||
|
\input{aggregates.tex}
|
||||||
|
\input{repository.tex}
|
||||||
|
\input{dto.tex}
|
||||||
|
\input{mapper.tex}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
\section{Adatbázis kapcsolat}
|
||||||
|
|
||||||
|
\begin{frame}{Kapcsolat alapok}
|
||||||
|
\begin{block}{Mi az adatbázis kapcsolat?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item A kliens és az adatbázis szerver közötti kommunikáció
|
||||||
|
\item Hozzáférés a lekérdezésekhez és tranzakciókhoz
|
||||||
|
\item Hitelesítés, jogosultság és titkosítás része a kapcsolatnak
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Kapcsolati paraméterek}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Host, port, adatbázis név
|
||||||
|
\item Felhasználónév, jelszó
|
||||||
|
\item SSL/TLS beállítások
|
||||||
|
\item Timeout és pool méretek
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{exampleblock}{Connection string példa}
|
||||||
|
\texttt{postgres://user:pass@localhost:5432/appdb}
|
||||||
|
\end{exampleblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Connection pooling}
|
||||||
|
\begin{block}{Miért kell?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item A kapcsolat fel- és leépítése drága
|
||||||
|
\item Pool újrahasznosítja a kapcsolatokat
|
||||||
|
\item Stabilabb teljesítmény csúcsterhelésen
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{alertblock}{Beállítási irányelv}
|
||||||
|
Túl kicsi pool lassít, túl nagy pool túlterheli a szervert.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Konfiguráció alkalmazásban}
|
||||||
|
\begin{block}{Környezeti változók}
|
||||||
|
\begin{lstlisting}[language=bash]
|
||||||
|
DATABASE_URL="postgres://user:pass@localhost:5432/appdb"
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Függőség kezelés}
|
||||||
|
A kapcsolat beállításait ne kódold a forráskódba, használj env fájlt.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Prisma megjegyzés}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Prisma-val a kapcsolat a \texttt{DATABASE\_URL}-on keresztül jön létre
|
||||||
|
\item A koncepció ugyanaz, csak a keretrendszer kezeli a részleteket
|
||||||
|
\item Ebben a részben nem feltétel a Prisma használata
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Hibakezelés és stabilitás}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Használj újracsatlakozást ideiglenes hibákra
|
||||||
|
\item Naplózd a kapcsolat hibákat, de ne írj ki jelszavakat
|
||||||
|
\item Tranzakcióknál kezeld a visszagörgetést
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
\section{DTO}
|
||||||
|
|
||||||
|
\begin{frame}{Mi az a DTO?}
|
||||||
|
\begin{block}{Data Transfer Object}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Adatok szállítására szolgáló objektum
|
||||||
|
\item Elválasztja az adatbázis sémát a kliens felőli interfésztől
|
||||||
|
\item Kontroll az átadott adatok felett
|
||||||
|
\item Validáció és transzformáció helye
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{exampleblock}{Példa}
|
||||||
|
Felhasználó entitás tartalmaz jelszó hash-t, de a DTO nem adja ki.
|
||||||
|
\end{exampleblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Miért használjunk DTO-t?}
|
||||||
|
\begin{block}{Előnyök}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Biztonság} - Érzékeny mezők elrejtése (pl. jelszó)
|
||||||
|
\item \textbf{Verziókezelés} - API változhat a DB séma változtatása nélkül
|
||||||
|
\item \textbf{Validáció} - Bejövő adatok ellenőrzése
|
||||||
|
\item \textbf{Dokumentáció} - Világos szerződés a kliens és szerver között
|
||||||
|
\item \textbf{Transzformáció} - Adatok átalakítása (pl. dátum formázás)
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma modell vs DTO}
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{Prisma Model}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
model User {
|
||||||
|
id Int @id
|
||||||
|
@default(autoincrement())
|
||||||
|
email String @unique
|
||||||
|
password String
|
||||||
|
name String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{UserDTO (kimenet)}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
email: "john@example.com",
|
||||||
|
name: "John Doe"
|
||||||
|
// password nincs benne!
|
||||||
|
// createdAt, updatedAt
|
||||||
|
// opcionális
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Input DTO - Létrehozás}
|
||||||
|
\begin{block}{CreateUserDTO - Constructor}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class CreateUserDTO {
|
||||||
|
constructor(data) {
|
||||||
|
this.email = data.email;
|
||||||
|
this.password = data.password;
|
||||||
|
this.name = data.name;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Input DTO - Validáció}
|
||||||
|
\begin{block}{CreateUserDTO - Validate módszer}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class CreateUserDTO {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
if (!this.email || !this.email.includes('@')) {
|
||||||
|
throw new Error('Érvénytelen email cím');
|
||||||
|
}
|
||||||
|
if (!this.password || this.password.length < 8) {
|
||||||
|
throw new Error('Jelszó túl rövid (min. 8 karakter)');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Output DTO - Válasz}
|
||||||
|
\begin{block}{UserResponseDTO}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UserResponseDTO {
|
||||||
|
constructor(user) {
|
||||||
|
this.id = user.id;
|
||||||
|
this.email = user.email;
|
||||||
|
this.name = user.name;
|
||||||
|
// password nincs másolva!
|
||||||
|
}
|
||||||
|
static fromPrismaUser(user) {
|
||||||
|
return new UserResponseDTO(user);
|
||||||
|
}
|
||||||
|
static fromManyPrismaUsers(users) {
|
||||||
|
return users.map(u => new UserResponseDTO(u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{DTO használata service-ben}
|
||||||
|
\begin{block}{UserService példa}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const prisma = require('./prisma');
|
||||||
|
const bcrypt = require('bcrypt');
|
||||||
|
class UserService {
|
||||||
|
async createUser(createUserDTO) {
|
||||||
|
createUserDTO.validate(); // Validálás
|
||||||
|
const hashedPassword = await bcrypt.hash(
|
||||||
|
createUserDTO.password, 10
|
||||||
|
); // Jelszó hash-elés
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email: createUserDTO.email,
|
||||||
|
password: hashedPassword,
|
||||||
|
name: createUserDTO.name
|
||||||
|
}// Mentés Prisma-val
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{DTO használata service-ben (folyt.)}
|
||||||
|
\begin{block}{UserService példa folytatás}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// DTO-vá alakítás visszaadás előtt
|
||||||
|
return UserResponseDTO.fromPrismaUser(user);
|
||||||
|
}
|
||||||
|
async getAllUsers() {
|
||||||
|
const users = await prisma.user.findMany();
|
||||||
|
return UserResponseDTO.fromManyPrismaUsers(users);
|
||||||
|
}
|
||||||
|
async getUserById(id) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
if (!user) return null;
|
||||||
|
return UserResponseDTO.fromPrismaUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Express integráció DTO-val}
|
||||||
|
\begin{block}{REST endpoint}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const express = require('express');
|
||||||
|
const userService = new UserService();
|
||||||
|
const app = express();
|
||||||
|
app.post('/api/users', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const createDTO = new CreateUserDTO(req.body);
|
||||||
|
const userDTO = await userService.createUser(createDTO);
|
||||||
|
res.status(201).json(userDTO);
|
||||||
|
} catch (error) {
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.get('/api/users', async (req, res) => {
|
||||||
|
const usersDTO = await userService.getAllUsers();
|
||||||
|
res.json(usersDTO);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Update DTO - Constructor}
|
||||||
|
\begin{block}{UpdateUserDTO}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UpdateUserDTO {
|
||||||
|
constructor(data) {
|
||||||
|
// Csak a megadott mezők kerülnek be
|
||||||
|
if (data.email !== undefined)
|
||||||
|
this.email = data.email;
|
||||||
|
if (data.name !== undefined)
|
||||||
|
this.name = data.name;
|
||||||
|
if (data.password !== undefined)
|
||||||
|
this.password = data.password;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Update DTO - Validáció}
|
||||||
|
\begin{block}{UpdateUserDTO - Validate}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UpdateUserDTO {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
if (this.email && !this.email.includes('@')) {
|
||||||
|
throw new Error('Érvénytelen email');
|
||||||
|
}
|
||||||
|
if (this.password && this.password.length < 8) {
|
||||||
|
throw new Error('Jelszó túl rövid');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Nested DTO - Relációkkal}
|
||||||
|
\begin{block}{PostWithAuthorDTO}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class PostWithAuthorDTO {
|
||||||
|
constructor(post) {
|
||||||
|
this.id = post.id;
|
||||||
|
this.title = post.title;
|
||||||
|
this.content = post.content;
|
||||||
|
this.createdAt = post.createdAt;
|
||||||
|
// Nested DTO az author-hoz
|
||||||
|
if (post.author) {
|
||||||
|
this.author = new UserResponseDTO(post.author);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static fromPrismaPost(post) {
|
||||||
|
return new PostWithAuthorDTO(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Nested DTO használata}
|
||||||
|
\begin{block}{Service metódus}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class PostService {
|
||||||
|
async getPostWithAuthor(postId) {
|
||||||
|
const post = await prisma.post.findUnique({
|
||||||
|
where: { id: postId },
|
||||||
|
include: {
|
||||||
|
author: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!post) return null;
|
||||||
|
|
||||||
|
return PostWithAuthorDTO.fromPrismaPost(post);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Validációs könyvtár - Joi}
|
||||||
|
\begin{block}{Joi telepítése}
|
||||||
|
\begin{lstlisting}[language=bash]
|
||||||
|
npm install joi
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Validációs séma}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const Joi = require('joi');
|
||||||
|
|
||||||
|
const createUserSchema = Joi.object({
|
||||||
|
email: Joi.string().email().required(),
|
||||||
|
password: Joi.string().min(8).required(),
|
||||||
|
name: Joi.string().optional()
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{DTO Joi validációval}
|
||||||
|
\begin{block}{CreateUserDTO sémával}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class CreateUserDTO {
|
||||||
|
constructor(data) {
|
||||||
|
const { error, value } =
|
||||||
|
createUserSchema.validate(data);
|
||||||
|
if (error)
|
||||||
|
throw new Error(error.details[0].message);
|
||||||
|
Object.assign(this, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Pagináció DTO}
|
||||||
|
\begin{block}{PaginatedResponseDTO}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class PaginatedResponseDTO {
|
||||||
|
constructor(data, total, page, pageSize) {
|
||||||
|
this.data = data;
|
||||||
|
this.pagination = {
|
||||||
|
total, page, pageSize,
|
||||||
|
totalPages: Math.ceil(total / pageSize)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Pagináció DTO használata}
|
||||||
|
\begin{block}{PaginatedResponseDTOUsage}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Használat
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
skip: (page - 1) * pageSize, take: pageSize
|
||||||
|
});
|
||||||
|
const total = await prisma.user.count();
|
||||||
|
return new PaginatedResponseDTO(
|
||||||
|
UserResponseDTO.fromManyPrismaUsers(users),
|
||||||
|
total, page, pageSize
|
||||||
|
);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Szűrési DTO - Constructor}
|
||||||
|
\begin{block}{UserFilterDTO}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UserFilterDTO {
|
||||||
|
constructor(query) {
|
||||||
|
this.email = query.email;
|
||||||
|
this.name = query.name;
|
||||||
|
this.page = parseInt(query.page) || 1;
|
||||||
|
this.pageSize = parseInt(query.pageSize) || 10;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Szűrési DTO - Prisma konverzió}
|
||||||
|
\begin{block}{toPrismaWhere módszer}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UserFilterDTO {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
toPrismaWhere() {
|
||||||
|
const where = {};
|
||||||
|
if (this.email)
|
||||||
|
where.email = { contains: this.email };
|
||||||
|
if (this.name)
|
||||||
|
where.name = { contains: this.name };
|
||||||
|
return where;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Szűrési DTO használata}
|
||||||
|
\begin{block}{Express endpoint szűréssel}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
app.get('/api/users', async (req, res) => {
|
||||||
|
const filterDTO = new UserFilterDTO(req.query);
|
||||||
|
const where = filterDTO.toPrismaWhere();
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
where,
|
||||||
|
skip: (filterDTO.page - 1) * filterDTO.pageSize,
|
||||||
|
take: filterDTO.pageSize
|
||||||
|
});
|
||||||
|
const total = await prisma.user.count({ where });
|
||||||
|
const result = new PaginatedResponseDTO(
|
||||||
|
UserResponseDTO.fromManyPrismaUsers(users),
|
||||||
|
total, filterDTO.page, filterDTO.pageSize
|
||||||
|
);
|
||||||
|
res.json(result);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Mapper függvények}
|
||||||
|
\begin{block}{Alternatív megközelítés - Pure functions // dto/user.mapper.js}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
function toUserResponseDTO(user) {
|
||||||
|
return {
|
||||||
|
id: user.id,
|
||||||
|
email: user.email,
|
||||||
|
name: user.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function toUserResponseDTOs(users) {
|
||||||
|
return users.map(toUserResponseDTO);
|
||||||
|
}
|
||||||
|
module.exports = {
|
||||||
|
toUserResponseDTO,
|
||||||
|
toUserResponseDTOs
|
||||||
|
};
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Mapper használata}
|
||||||
|
\begin{block}{Service-ben}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const { toUserResponseDTO, toUserResponseDTOs }
|
||||||
|
= require('./dto/user.mapper');
|
||||||
|
class UserService {
|
||||||
|
async getAllUsers() {
|
||||||
|
const users = await prisma.user.findMany();
|
||||||
|
return toUserResponseDTOs(users);
|
||||||
|
}
|
||||||
|
async getUserById(id) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
if (!user) return null;
|
||||||
|
return toUserResponseDTO(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{DTO Best Practices}
|
||||||
|
\begin{block}{Ajánlások}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Külön DTO minden use case-hez (Create, Update, Response)
|
||||||
|
\item Ne add vissza közvetlenül a Prisma modellt
|
||||||
|
\item Validálj minden bejövő adatot
|
||||||
|
\item Használj validációs könyvtárat (Joi, Yup, Zod)
|
||||||
|
\item Tartsd egyszerűnek - ne túlbonyolítsd
|
||||||
|
\item Dokumentáld a DTO mezőket
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{DTO vs Prisma Select}
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{Prisma Select}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Egyszerű mezők kiválasztása
|
||||||
|
\item Kevesebb kód
|
||||||
|
\item Nincs validáció
|
||||||
|
\item DB szintű szűrés
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{DTO}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Komplex transzformációk
|
||||||
|
\item Validáció és logika
|
||||||
|
\item Típusbiztonság
|
||||||
|
\item API verziókezelés
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
|
||||||
|
\vspace{0.5cm}
|
||||||
|
|
||||||
|
\begin{alertblock}{Kombináld őket!}
|
||||||
|
Használd a Prisma select-et a DB szinten, és DTO-t a transzformációhoz.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás}
|
||||||
|
\begin{block}{DTO előnyei}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Biztonság} - Érzékeny adatok elrejtése
|
||||||
|
\item \textbf{Validáció} - Adatok ellenőrzése
|
||||||
|
\item \textbf{Transzformáció} - Adatok átalakítása
|
||||||
|
\item \textbf{Dokumentáció} - Világos API szerződés
|
||||||
|
\item \textbf{Karbantarthatóság} - Könnyebb változtatások
|
||||||
|
\item \textbf{Tesztelhetőség} - Egyszerűbb unit tesztek
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
\section{Mapper}
|
||||||
|
|
||||||
|
\begin{frame}{Mapper minta szerepe}
|
||||||
|
\begin{block}{Mi a mapper?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Átalakítás domain objektumok és DTO-k között
|
||||||
|
\item Elválasztja az adatformátumot az üzleti modelltől
|
||||||
|
\item Központi helyen tartja az átalakítás logikáját
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Miért hasznos?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Csökkenti a duplikált átalakításokat
|
||||||
|
\item Stabil API-szerződések (DTO-k) a változó domain mellett
|
||||||
|
\item Tesztelhető, moduláris és újrafelhasználható kód
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Egyszerű mapper példa}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Domain objektum
|
||||||
|
const user = {
|
||||||
|
id: 1, name: "John Doe",
|
||||||
|
email: "john@example.com", passwordHash: "..."
|
||||||
|
};
|
||||||
|
|
||||||
|
// Mapper függvény
|
||||||
|
const toUserDto = (u) => ({
|
||||||
|
id: u.id, name: u.name, email: u.email
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Kétirányú mapper}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const toUserEntity = (dto) => ({
|
||||||
|
id: dto.id ?? undefined,
|
||||||
|
name: dto.name,
|
||||||
|
email: dto.email
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{block}{Megjegyzés}
|
||||||
|
Írhatsz külön mappert bejövő és kimenő adatokra.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Mapper és repository}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Repository a tárolást kezeli
|
||||||
|
\item Mapper az objektumok formátumát kezeli
|
||||||
|
\item Két külön felelősségi kör
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Jó gyakorlatok}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Ne másolj át érzékeny mezőket (pl. jelszó)
|
||||||
|
\item Tartsd egy helyen az átalakításokat
|
||||||
|
\item Használj tiszta függvényeket és kis helper-eket
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
\section{ORM}
|
||||||
|
|
||||||
|
\begin{frame}{Mi az ORM?}
|
||||||
|
\begin{block}{Object-Relational Mapping}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Objektumok és relációs táblák közötti leképezés
|
||||||
|
\item Osztályok \textrightarrow{} táblákat, objektumok \textrightarrow{} sorokat jelentenek
|
||||||
|
\item Átfedő absztrakció az SQL felett
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{exampleblock}{Példa}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{User} osztály \textrightarrow{} \texttt{users} tábla
|
||||||
|
\item \texttt{user.email} mezők \textrightarrow{} \texttt{email} oszlop
|
||||||
|
\end{itemize}
|
||||||
|
\end{exampleblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Miért használunk ORM-et?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Kevesebb boilerplate SQL, gyorsabb fejlesztés
|
||||||
|
\item Típusbiztonság, IDE támogatás, egységes API
|
||||||
|
\item Adatbázis-portabilitás (pl. PostgreSQL \textrightarrow{} MySQL)
|
||||||
|
\item Biztonság: paraméterizált lekérdezések alapból
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Korlátai és kockázatai}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Teljesítmény: rosszul írt lekérdezések, N+1 probléma
|
||||||
|
\item "Leaky abstraction": komplex SQL-t nehéz elrejteni
|
||||||
|
\item Nem minden adatbázis funkció elérhető el könnyen
|
||||||
|
\item Extra tanulási görbe és konfiguráció
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Alap fogalmak}
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{Entity}
|
||||||
|
Egy tábla sorát leíró objektum.
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Repository}
|
||||||
|
Adathozzáférési réteget kapszuláz.
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{Unit of Work}
|
||||||
|
Változások követése és egyben mentése.
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Session/Context}
|
||||||
|
Kapcsolat az adatbázissal, cache és tranzakciók.
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Kapcsolatok leképezése}
|
||||||
|
\begin{itemize}
|
||||||
|
\item One-to-One: \texttt{User} \textrightarrow{} \texttt{Profile}
|
||||||
|
\item One-to-Many: \texttt{Author} \textrightarrow{} \texttt{Post[]}
|
||||||
|
\item Many-to-Many: \texttt{Student} \textrightarrow{} \texttt{Course[]}
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{Felelősség}
|
||||||
|
A helyes kulcsok, indexek és megszorítások ugyanúgy fontosak, mint ORM nélkül.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{SQL vs ORM lekérdezés}
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{SQL}
|
||||||
|
\begin{lstlisting}[language=SQL]
|
||||||
|
SELECT id, name, email
|
||||||
|
FROM users
|
||||||
|
WHERE active = true
|
||||||
|
ORDER BY name ASC;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{ORM (példa)}
|
||||||
|
\begin{lstlisting}[language=Java]
|
||||||
|
List<User> users = db.users()
|
||||||
|
.where(u -> u.active == true)
|
||||||
|
.orderBy(u -> u.name)
|
||||||
|
.select("id", "name", "email")
|
||||||
|
.toList();
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Lazy vs Eager betöltés}
|
||||||
|
\begin{block}{Lazy loading}
|
||||||
|
Csak akkor tölt be kapcsolt adatokat, amikor elérjük őket.
|
||||||
|
\end{block}
|
||||||
|
\begin{block}{Eager loading}
|
||||||
|
Előre betölt kapcsolatokat egy lekérdezéssel (JOIN).
|
||||||
|
\end{block}
|
||||||
|
\begin{alertblock}{N+1 probléma}
|
||||||
|
Túl sok lekérdezés keletkezik, ha minden kapcsolt adat külön jön le.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Jó gyakorlatok}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Használj migrációkat a séma kezelésére
|
||||||
|
\item Mérj teljesítményt és ne félj nyers SQL-t írni
|
||||||
|
\item Állíts be indexeket a gyakori szűrésekhez
|
||||||
|
\item Tranzakciókkal biztosítsd a konzisztenciát
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,413 @@
|
|||||||
|
\section{Prisma ORM}
|
||||||
|
|
||||||
|
\begin{frame}{Mi az a Prisma?}
|
||||||
|
\begin{block}{Prisma}
|
||||||
|
Modern ORM (Object-Relational Mapping) Node.js és TypeScript környezethez
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Főbb jellemzők}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Típusbiztos adatbázis kliens
|
||||||
|
\item Deklaratív séma definíció
|
||||||
|
\item Automatikus migráció kezelés
|
||||||
|
\item Intuitív API CRUD műveletekhez
|
||||||
|
\item Támogatott adatbázisok: PostgreSQL, MySQL, SQLite, MongoDB, stb.
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma telepítése}
|
||||||
|
\begin{block}{NPM telepítés}
|
||||||
|
\begin{lstlisting}[language=bash]
|
||||||
|
# Prisma CLI telepítése dev dependencyként
|
||||||
|
npm install prisma --save-dev
|
||||||
|
|
||||||
|
# Prisma Client telepítése
|
||||||
|
npm install @prisma/client
|
||||||
|
|
||||||
|
# Prisma inicializálása
|
||||||
|
npx prisma init
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Létrejövő fájlok}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{prisma/schema.prisma} - Adatbázis séma
|
||||||
|
\item \texttt{.env} - Környezeti változók (pl. DATABASE\_URL)
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma Schema alapok}
|
||||||
|
\begin{block}{schema.prisma}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
}
|
||||||
|
datasource db {
|
||||||
|
provider = "postgresql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
email String @unique
|
||||||
|
name String?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
updatedAt DateTime @updatedAt
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma migrációk}
|
||||||
|
\begin{block}{Adatbázis migráció létrehozása és futtatása}
|
||||||
|
\begin{lstlisting}[language=bash]
|
||||||
|
# Migráció létrehozása
|
||||||
|
npx prisma migrate dev --name init
|
||||||
|
|
||||||
|
# Prisma Client újragenerálása
|
||||||
|
npx prisma generate
|
||||||
|
|
||||||
|
# Adatbázis vizualizáció
|
||||||
|
npx prisma studio
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma Client inicializálása}
|
||||||
|
\begin{block}{prisma.js}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const { PrismaClient } = require('@prisma/client');
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
module.exports = prisma;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{alertblock}{Singleton pattern}
|
||||||
|
Érdemes egyetlen Prisma Client példányt használni az alkalmazásban.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Create - Létrehozás}
|
||||||
|
\begin{block}{Egy rekord létrehozása}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const prisma = require('./prisma');
|
||||||
|
|
||||||
|
async function createUser() {
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
email: 'john@example.com',
|
||||||
|
name: 'John Doe'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(user);
|
||||||
|
// { id: 1, email: 'john@example.com', name: 'John Doe', ... }
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Create - Több rekord}
|
||||||
|
\begin{block}{createMany használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function createManyUsers() {
|
||||||
|
const users = await prisma.user.createMany({
|
||||||
|
data: [
|
||||||
|
{ email: 'alice@example.com', name: 'Alice' },
|
||||||
|
{ email: 'bob@example.com', name: 'Bob' },
|
||||||
|
{ email: 'charlie@example.com', name: 'Charlie' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${users.count} felhasználó létrehozva`);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - Összes rekord}
|
||||||
|
\begin{block}{findMany használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getAllUsers() {
|
||||||
|
const users = await prisma.user.findMany();
|
||||||
|
console.log(users);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rendezéssel
|
||||||
|
async function getUsersSorted() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - Egy rekord}
|
||||||
|
\begin{block}{findUnique és findFirst}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
// Egyedi mező alapján (pl. id, email)
|
||||||
|
async function getUserById(id) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: id }
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
// Első találat
|
||||||
|
async function getUserByName(name) {
|
||||||
|
const user = await prisma.user.findFirst({
|
||||||
|
where: { name: name }
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - Szűrés}
|
||||||
|
\begin{block}{Where feltételek}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function filterUsers() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
where: {
|
||||||
|
email: {
|
||||||
|
contains: '@example.com'
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
startsWith: 'J'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Read - Mezők kiválasztása}
|
||||||
|
\begin{block}{Select használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getUserEmails() {
|
||||||
|
const users = await prisma.user.findMany({
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
email: true,
|
||||||
|
name: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return users;
|
||||||
|
// [{ id: 1, email: '...', name: '...' }, ...]
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Update - Módosítás}
|
||||||
|
\begin{block}{Egy rekord frissítése}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function updateUser(id) {
|
||||||
|
const user = await prisma.user.update({
|
||||||
|
where: { id: id },
|
||||||
|
data: {
|
||||||
|
name: 'Jane Doe',
|
||||||
|
email: 'jane@example.com'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Update - Több rekord}
|
||||||
|
\begin{block}{updateMany használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function updateManyUsers() {
|
||||||
|
const result = await prisma.user.updateMany({
|
||||||
|
where: {
|
||||||
|
email: {
|
||||||
|
contains: '@old-domain.com'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
email: 'updated@new-domain.com'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${result.count} rekord frissítve`);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Delete - Törlés}
|
||||||
|
\begin{block}{Egy rekord törlése}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function deleteUser(id) {
|
||||||
|
const user = await prisma.user.delete({
|
||||||
|
where: { id: id }
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Delete - Több rekord}
|
||||||
|
\begin{block}{deleteMany használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function deleteInactiveUsers() {
|
||||||
|
const result = await prisma.user.deleteMany({
|
||||||
|
where: {
|
||||||
|
createdAt: {
|
||||||
|
lt: new Date('2020-01-01')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${result.count} felhasználó törölve`);
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Relációk - Schema}
|
||||||
|
\begin{block}{Egy-a-többhöz kapcsolat}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
posts Post[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Post {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
title String
|
||||||
|
content String?
|
||||||
|
authorId Int
|
||||||
|
author User @relation(fields: [authorId],
|
||||||
|
references: [id])
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Relációk - Include}
|
||||||
|
\begin{block}{Kapcsolt adatok lekérdezése}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function getUserWithPosts(userId) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: { id: userId },
|
||||||
|
include: {
|
||||||
|
posts: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(user);
|
||||||
|
// { id: 1, name: 'John', posts: [...] }
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Relációk - Nested Write}
|
||||||
|
\begin{block}{Kapcsolt rekordok létrehozása}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function createUserWithPosts() {
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: {
|
||||||
|
name: 'John Doe',
|
||||||
|
email: 'john@example.com',
|
||||||
|
posts: {
|
||||||
|
create: [
|
||||||
|
{ title: 'First Post', content: 'Hello!' },
|
||||||
|
{ title: 'Second Post', content: 'World!' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Tranzakciók}
|
||||||
|
\begin{block}{Több művelet egy tranzakcióban}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function transferPosts(fromUserId, toUserId) {
|
||||||
|
const result = await prisma.$transaction([
|
||||||
|
prisma.post.updateMany({
|
||||||
|
where: { authorId: fromUserId },
|
||||||
|
data: { authorId: toUserId }
|
||||||
|
}),
|
||||||
|
prisma.user.update({
|
||||||
|
where: { id: fromUserId },
|
||||||
|
data: { name: 'Archived User' }
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Express integráció példa}
|
||||||
|
\begin{block}{REST API endpoint Prisma-val}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
const express = require('express');
|
||||||
|
const prisma = require('./prisma');
|
||||||
|
const app = express();
|
||||||
|
app.use(express.json());
|
||||||
|
app.get('/users', async (req, res) => {
|
||||||
|
const users = await prisma.user.findMany();
|
||||||
|
res.json(users);
|
||||||
|
});
|
||||||
|
app.post('/users', async (req, res) => {
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: req.body
|
||||||
|
});
|
||||||
|
res.json(user);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Hibakezelés}
|
||||||
|
\begin{block}{Try-catch használata}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
async function safeCreateUser(data) {
|
||||||
|
try {
|
||||||
|
const user = await prisma.user.create({
|
||||||
|
data: data
|
||||||
|
});
|
||||||
|
return { success: true, user };
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 'P2002') {
|
||||||
|
return { success: false,
|
||||||
|
error: 'Email már létezik' };
|
||||||
|
}
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Prisma előnyei}
|
||||||
|
\begin{block}{Miért használjuk?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Típusbiztonság} - Auto-complete és típus ellenőrzés
|
||||||
|
\item \textbf{Egyszerű API} - Intuitív, könnyen tanulható
|
||||||
|
\item \textbf{Migráció kezelés} - Automatikus séma szinkronizáció
|
||||||
|
\item \textbf{Teljesítmény} - Optimalizált query-k
|
||||||
|
\item \textbf{Prisma Studio} - Vizuális adatbázis böngésző
|
||||||
|
\item \textbf{Jó dokumentáció} - Részletes guide-ok és példák
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
\section{Repository}
|
||||||
|
|
||||||
|
\begin{frame}{Repository minta lényege}
|
||||||
|
\begin{block}{Mire jó?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Elválasztja az adat-hozzáférést az üzleti logikától
|
||||||
|
\item Egységes API az adatok kezelésére
|
||||||
|
\item Könnyebben tesztelhető kód
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{IRepository és konkret repository}
|
||||||
|
\begin{block}{IRepository}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Absztrakt interfész CRUD műveletekkel
|
||||||
|
\item Pl. \texttt{findAll}, \texttt{findById}, \texttt{create}, \texttt{update}, \texttt{delete}
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{exampleblock}{Konkret repository}
|
||||||
|
Az interfész implementációja (pl. \texttt{UserRepository}).
|
||||||
|
\end{exampleblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{IRepository példa (TypeScript)}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
interface IRepository<T> {
|
||||||
|
findAll(): Promise<T[]>;
|
||||||
|
findById(id: number): Promise<T | null>;
|
||||||
|
create(data: T): Promise<T>;
|
||||||
|
update(id: number, data: Partial<T>): Promise<T>;
|
||||||
|
delete(id: number): Promise<void>;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma repository példa}
|
||||||
|
\begin{block}{Prisma Client}
|
||||||
|
A Prisma Client kezeli a kapcsolatot és az SQL generálást.
|
||||||
|
\end{block}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
class UserRepository implements IRepository<User> {
|
||||||
|
constructor(private prisma: PrismaClient) {}
|
||||||
|
findAll() {
|
||||||
|
return this.prisma.user.findMany();
|
||||||
|
}
|
||||||
|
findById(id: number) {
|
||||||
|
return this.prisma.user.findUnique({ where: { id } });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Prisma repository példa folytatás}
|
||||||
|
\begin{lstlisting}[language=JavaScript]
|
||||||
|
create(data: User) {
|
||||||
|
return this.prisma.user.create({ data });
|
||||||
|
}
|
||||||
|
|
||||||
|
update(id: number, data: Partial<User>) {
|
||||||
|
return this.prisma.user.update({ where: { id }, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(id: number) {
|
||||||
|
await this.prisma.user.delete({ where: { id } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Mikor hasznos?}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Ha több adatforrás van (SQL, API, cache)
|
||||||
|
\item Ha szeretnél változtatható adat-hozzáférést
|
||||||
|
\item Ha szükség van mock-olható IRepositories-re tesztekhez
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
Reference in New Issue
Block a user