Backend

Command-ok
Commandok a backend létrehozásához.

git clone https://github.com/hgabor/nestjs-keret-2025
npm i
npm i class-validator class-transformer
npx prisma db pull
npx prisma db push
npx prisma generate
npm i @faker-js/faker
nest g resources model1_name
            
schema.prisma
Példa schema.prisma fájlra

model1_name    model2_name[]
model [model2_name] {
id BigInt @id @default(autoincrement()) @db.UnsignedBigInt
price BigInt
model1_name   model1_name @relation(fields: [model1_id], references: [id])
model1_name BigInt  @db.UnsignedBigInt
}
Seed.ts
Példa seed.ts fájlra
Faker types

import {faker} from '@faker-js/faker';
import { PrismaClient } from 'generated/prisma/client';
import "dotenv/config";

const prisma = new PrismaClient();
async function main() {
    for (let index = 0; index < 10; index++) {
       await prisma.model2_name.create({
        data:{
            model1_name: faker.number.bigInt({ min: 1, max: 10 }),
            price: faker.number.bigInt({ min: 1000, max: 5000 }),
        }
       })
        
    }
}
main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });
                    
                
[name].service.ts
Api metódusok

import { PrismaService } from 'src/prisma.service';

constructor(private readonly prismaService: PrismaService) {}


  create(createCourseDto: CreateCourseDto) {
    return this.prismaService.courses.create({
      data: createCourseDto
        });
    }
  async findAll() {
    const courses = await this.prismaService.courses.findMany();
    return courses;
  }
  remove(id: number) {
    const course = this.prismaService.courses.delete({
      where: {id},
    });
    return course;
  }
                

Frontend

Command-ok
Commandok a frontend létrehozásához.

npm create vite@latest [project-name]       //itt válaszd a vanilla-t és a TypeScript-et

cd [project-name]

npm i

npm run dev
            
Adatok fetchelése
Példa

const data = await fetch('http://localhost:3000');
const test = await data.json();
console.log(test);
            
Kiíratás
Példa kiíratás listával

const ul = document.createElement("ul");
test.forEach((valami) => {
    const listItem = document.createElement("li");
    listItem.textContent = valami.title;
    ul.appendChild(listItem);
});
document.body.appendChild(ul);
            
Kiíratás
Példa kiíratás táblázatban

const table = document.createElement("table");
const headerRow = document.createElement("tr");
headerRow.innerHTML = "CímHosszExplicit";
table.appendChild(headerRow);

test.forEach((valami) => {
    const row = document.createElement("tr");
    row.innerHTML = `${valami.title}${valami.duration}${valami.explicit}`;
    table.appendChild(row);
});

document.body.appendChild(table);
            

React

useState használata
Állapotkezelés React-ben TypeScript-tel

import { useState } from 'react';

// típusos tömb állapot
const [courses, setCourses] = useState([]);

// string vagy null állapot
const [error, setError] = useState(null);

// objektum alapú állapot (ID -> string)
const [applyStatus, setApplyStatus] = useState>({});
            
useEffect - Adatok betöltése
Adatok lekérése a backendről komponens betöltésekor

import { useEffect, useState } from 'react';

useEffect(() => {
    const loadData = async () => {
        try {
            const res = await fetch('http://localhost:3000/api/courses');
            const data = await res.json();
            
            if (!res.ok) {
                setError(data.error);
                return;
            }
            
            setCourses(data.data);
        } catch (err) {
            setError('Hiba az adatok lekérésekor!');
        }
    };
    
    loadData();
}, []); // üres dependency array = csak egyszer fut le
            
POST kérés kezelése
Adatok küldése a backend-nek

const handleSubmit = async (id: number) => {
    try {
        const res = await fetch(`http://localhost:3000/api/courses/${id}/apply`, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ userId: 123 })
        });
        
        const data = await res.json();
        
        if (res.status === 201) {
            setApplyStatus(prev => ({ 
                ...prev, 
                [id]: 'Sikeres jelentkezés!' 
            }));
        } else {
            setApplyStatus(prev => ({ 
                ...prev, 
                [id]: data.error 
            }));
        }
    } catch (err) {
        setApplyStatus(prev => ({ 
            ...prev, 
            [id]: 'Hiba a jelentkezéskor!' 
        }));
    }
};
            
Segédfüggvény - Switch/Case
Típus alapú képválasztás

const getTypeImage = (type: string): string => {
    switch(type) {
        case 'solo': return '/images/solo.svg';
        case 'partner': return '/images/partner.svg';
        case 'group': return '/images/group.svg';
        default: return '';
    }
};
            
Lista renderelés map()-pel
Tömb elemeinek megjelenítése JSX-ben

{courses.map(course => (

{course.name}

Hossz: {course.length} perc

Tanár: {course.instructor}

{course.type}
))}
Feltételes renderelés és osztályok
Dinamikus tartalomkezelés React-ben

// Hibaüzenet megjelenítése, ha van
{error && 
{error}
} // Dinamikus CSS osztályok

{applyStatus[course.id] || ""}

TypeScript interface
Típusos adatszerkezetek definiálása

interface Course {
    id: number;
    name: string;
    length: number;
    instructor: string;
    type: 'solo' | 'partner' | 'group';
}

// használat
const [courses, setCourses] = useState([]);
            
Bootstrap import
Bootstrap CSS betöltése React projektben

import 'bootstrap/dist/css/bootstrap.min.css';