diff --git a/Backend_ppt/Database/aggregates.tex b/Backend_ppt/Database/aggregates.tex new file mode 100644 index 0000000..1cbd077 --- /dev/null +++ b/Backend_ppt/Database/aggregates.tex @@ -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} diff --git a/Backend_ppt/Database/crud_operations.tex b/Backend_ppt/Database/crud_operations.tex new file mode 100644 index 0000000..1abbec2 --- /dev/null +++ b/Backend_ppt/Database/crud_operations.tex @@ -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} diff --git a/Backend_ppt/Database/database.pdf b/Backend_ppt/Database/database.pdf new file mode 100644 index 0000000..e8769e5 Binary files /dev/null and b/Backend_ppt/Database/database.pdf differ diff --git a/Backend_ppt/Database/database.tex b/Backend_ppt/Database/database.tex new file mode 100644 index 0000000..d4316cf --- /dev/null +++ b/Backend_ppt/Database/database.tex @@ -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} \ No newline at end of file diff --git a/Backend_ppt/Database/database_connection.tex b/Backend_ppt/Database/database_connection.tex new file mode 100644 index 0000000..a94d23b --- /dev/null +++ b/Backend_ppt/Database/database_connection.tex @@ -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} diff --git a/Backend_ppt/Database/dto.tex b/Backend_ppt/Database/dto.tex new file mode 100644 index 0000000..2d167ac --- /dev/null +++ b/Backend_ppt/Database/dto.tex @@ -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} diff --git a/Backend_ppt/Database/mapper.tex b/Backend_ppt/Database/mapper.tex new file mode 100644 index 0000000..055e152 --- /dev/null +++ b/Backend_ppt/Database/mapper.tex @@ -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} diff --git a/Backend_ppt/Database/orm.tex b/Backend_ppt/Database/orm.tex new file mode 100644 index 0000000..5f91a0a --- /dev/null +++ b/Backend_ppt/Database/orm.tex @@ -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 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} diff --git a/Backend_ppt/Database/prisma.tex b/Backend_ppt/Database/prisma.tex new file mode 100644 index 0000000..1fa4b47 --- /dev/null +++ b/Backend_ppt/Database/prisma.tex @@ -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} diff --git a/Backend_ppt/Database/repository.tex b/Backend_ppt/Database/repository.tex new file mode 100644 index 0000000..7122644 --- /dev/null +++ b/Backend_ppt/Database/repository.tex @@ -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 { + findAll(): Promise; + findById(id: number): Promise; + create(data: T): Promise; + update(id: number, data: Partial): Promise; + delete(id: number): Promise; +} + \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 { + 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) { + 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}