database added

This commit is contained in:
magdo
2026-02-08 00:51:17 +01:00
parent 863f76934c
commit 6afdef6a30
10 changed files with 1736 additions and 0 deletions
+354
View File
@@ -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}
+130
View File
@@ -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.
+26
View File
@@ -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}
+486
View File
@@ -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}
+64
View File
@@ -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}
+118
View File
@@ -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}
+413
View File
@@ -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}
+79
View File
@@ -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}