NestJS-yritysbackendien API:t · Oppitunti

CRUD-operaatiot TypeORMilla

Kehittäkää entiteeteille täydet Create-, Read-, Update- ja Delete-toiminnot TypeORM-repositoryjen avulla.

Oppitunti 2/311 vaihetta

CRUD-operaatiot TypeORMilla on ilmainen NestJS-yritysbackendien API:t-oppitunti CoddyKitissä. Tämä on oppitunti 2/3. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu NestJS-yritysbackendien API:t-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. NestJS-yritysbackendien API:t-kurssilla on yhteensä 3 oppituntia.

CRUD-perusteet TypeORMilla

Tervetuloa! Tässä oppitunnissa opitte hallitsemaan CRUD-toimintoja TypeORMilla, joka on tehokas TypeScriptin Object-Relational Mapper.

CRUD tulee sanoista Create, Read, Update ja Delete. Nämä ovat tietojen hallinnan neljä perustoimintoa lähes kaikissa sovelluksissa.

TypeORM helpottaa tietokannan käsittelyä yhdistämällä tietokantarivit TypeScript-olioihin, mikä tekee näistä toiminnoista intuitiivisia.

Product-entiteetin määrittäminen

Ennen CRUD-toimintojen suorittamista tarvitsemme entiteetin. Entiteetti on luokka, joka vastaa suoraan tietokantataulua. Kuvitellaan esimerkkejämme varten yksinkertainen Product-entiteetti.

TypeORMissa sen rakenne ja vastaavuus tietokantaskeemaan määritetään esimerkiksi dekoraattoreilla @Entity(), @PrimaryGeneratedColumn() ja @Column().

Repositoryn ymmärtäminen

TypeORM käyttää repository-mallia. Repository on erikoistunut kokoelma, joka huolehtii tietyn entiteetin tietojen käytöstä (esimerkiksi ProductRepository Product-entiteeteille).

Repository injektoidaan palveluihin, ja sen metodeilla suoritetaan CRUD-toimintoja. Se piilottaa monimutkaiset tietokantakyselyt.

Luo: uusien tuotteiden lisääminen

Kun tietokantaan lisätään uusia tietoja, käytetään ensisijaisesti repon .save()-metodia. Se käsittelee älykkäästi sekä uudet lisäykset että olemassa olevien entiteettien päivitykset.

Tässä on yksinkertaistettu esimerkki uuden tuotteen luomisesta:

class Product {
  id: number;
  name: string;
  price: number;
  constructor(name: string, price: number, id: number = 0) {
    this.id = id;
    this.name = name;
    this.price = price;
  }
}

class MockProductRepository {
  private products: Product[] = [];
  private nextId = 1;

  async save(product: Product): Promise<Product> {
    if (product.id === 0) { // Simulate new product
      product.id = this.nextId++;
      this.products.push(product);
    } else { // Simulate update
      const index = this.products.findIndex(p => p.id === product.id);
      if (index > -1) { this.products[index] = product; }
    }
    return product;
  }

  async find(): Promise<Product[]> {
    return this.products;
  }
}

async function main() {
  const repo = new MockProductRepository();
  console.log("--- Creating a Product ---");
  const newProduct = await repo.save(new Product("Keyboard", 75));
  console.log("Created:", newProduct);

  console.log("\n--- Current Products ---");
  const allProducts = await repo.find();
  console.log(allProducts);
}

main();

Lue: tuotteiden hakeminen

Tietojen lukeminen on olennainen toiminto. TypeORM-repositoryt tarjoavat useita metodeja entiteettien hakemiseen:

  • .find(): hakee kaikki annetut ehdot täyttävät entiteetit.
  • .findOneBy(): hakee yhden entiteetin yksinkertaisen ehdon perusteella (esimerkiksi tunnuksen perusteella).
  • .findBy(): hakee useita entiteettejä yksinkertaisten ehtojen perusteella.

Katsotaan, miten tuotteita haetaan:

class Product {
  id: number;
  name: string;
  price: number;
  constructor(name: string, price: number, id: number = 0) {
    this.id = id;
    this.name = name;
    this.price = price;
  }
}

class MockProductRepository {
  private products: Product[] = [];
  private nextId = 1;

  async save(product: Product): Promise<Product> {
    if (product.id === 0) { product.id = this.nextId++; this.products.push(product); }
    else { const index = this.products.findIndex(p => p.id === product.id);
      if (index > -1) { this.products[index] = product; } }
    return product;
  }

  async find(): Promise<Product[]> { return this.products; }
  async findOneBy(criteria: Partial<Product>): Promise<Product | undefined> {
    return this.products.find(p =>
      Object.keys(criteria).every(key => p[key as keyof Product] === criteria[key as keyof Product])
    );
  }
}

async function main() {
  const repo = new MockProductRepository();
  const product1 = await repo.save(new Product("Mouse", 25));
  const product2 = await repo.save(new Product("Monitor", 300));

  console.log("--- Finding All Products ---");
  let allProducts = await repo.find();
  console.log("All:", allProducts);

  console.log("\n--- Finding One Product by ID ---");
  const foundProduct = await repo.findOneBy({ id: product1.id });
  console.log("Found by ID:", foundProduct);

  console.log("\n--- Finding One Product by Name ---");
  const namedProduct = await repo.findOneBy({ name: "Monitor" });
  console.log("Found by Name:", namedProduct);
}

main();

Päivitä: tuotetietojen muokkaaminen

Olemassa olevien tietojen muokkaamiseen on kaksi pääasiallista tapaa:

  • Hae, muokkaa, tallenna: Hae entiteetti, päivitä sen ominaisuudet ja kutsu sitten muokatun entiteetin .save()-metodia.
  • Suora päivitys: Käytä .update(criteria, partialEntity)-metodia päivittääksesi tiettyjä sarakkeita ehdot täyttävistä entiteeteistä lataamatta niitä ensin muistiin. Tämä on tehokkaampaa suurissa päivityksissä.
class Product {
  id: number;
  name: string;
  price: number;
  constructor(name: string, price: number, id: number = 0) {
    this.id = id;
    this.name = name;
    this.price = price;
  }
}

class MockProductRepository {
  private products: Product[] = [];
  private nextId = 1;

  async save(product: Product): Promise<Product> {
    if (product.id === 0) { product.id = this.nextId++; this.products.push(product); }
    else { const index = this.products.findIndex(p => p.id === product.id);
      if (index > -1) { this.products[index] = product; } }
    return product;
  }

  async findOneBy(criteria: Partial<Product>): Promise<Product | undefined> {
    return this.products.find(p =>
      Object.keys(criteria).every(key => p[key as keyof Product] === criteria[key as keyof Product])
    );
  }
  
  async update(id: number, partialEntity: Partial<Product>): Promise<void> {
    const index = this.products.findIndex(p => p.id === id);
    if (index > -1) {
      this.products[index] = { ...this.products[index], ...partialEntity };
    }
  }
}

async function main() {
  const repo = new MockProductRepository();
  const product = await repo.save(new Product("Headphones", 150));
  console.log("Initial product:", product);

  console.log("\n--- Updating Product Price ---");
  let fetchedProduct = await repo.findOneBy({ id: product.id });
  if (fetchedProduct) {
    fetchedProduct.price = 140; 
    await repo.save(fetchedProduct);
  }
  console.log("Updated via save:", await repo.findOneBy({ id: product.id }));

  const product2 = await repo.save(new Product("Webcam", 80));
  console.log("Initial product 2:", product2);
  await repo.update(product2.id, { price: 70, name: "HD Webcam" });
  console.log("Updated via update:", await repo.findOneBy({ id: product2.id }));
}

main();

Poista: tuotteiden poistaminen

Entiteettien poistamiseen tietokannasta TypeORM tarjoaa metodit .delete() ja .remove():

  • .delete(criteria): poistaa entiteettejä tunnuksen tai tiettyjen ehtojen perusteella. Se on tehokas, koska entiteettejä ei ensin ladata muistiin.
  • .remove(entityOrEntities): poistaa entiteettejä, jotka on jo ladattu muistiin (eli käytettävissä on koko entiteettiolio).

Poistetaan tuote esimerkkitietokannastamme:

class Product {
  id: number;
  name: string;
  price: number;
  constructor(name: string, price: number, id: number = 0) {
    this.id = id;
    this.name = name;
    this.price = price;
  }
}

class MockProductRepository {
  private products: Product[] = [];
  private nextId = 1;

  async save(product: Product): Promise<Product> {
    if (product.id === 0) { product.id = this.nextId++; this.products.push(product); }
    else { const index = this.products.findIndex(p => p.id === product.id);
      if (index > -1) { this.products[index] = product; } }
    return product;
  }

  async find(): Promise<Product[]> { return this.products; }

  async delete(id: number): Promise<void> {
    this.products = this.products.filter(p => p.id !== id);
  }
  
  async remove(product: Product): Promise<Product> {
    this.products = this.products.filter(p => p.id !== product.id);
    return product; 
  }
}

async function main() {
  const repo = new MockProductRepository();
  const productA = await repo.save(new Product("Pen", 2));
  const productB = await repo.save(new Product("Notebook", 5));
  console.log("Initial products:", await repo.find());

  console.log("\n--- Deleting Product A by ID ---");
  await repo.delete(productA.id);
  console.log("Products after delete:", await repo.find());

  console.log("\n--- Removing Product B (by entity) ---");
  await repo.remove(productB);
  console.log("Products after remove:", await repo.find());
}

main();

CRUD NestJS-palvelussa

Oikeassa NestJS-sovelluksessa nämä repository-metodit integroitaisiin palveluun. Palvelu tarjoaisi korkeamman tason metodeja, jotka toteuttavat tietyn liiketoimintalogiikan ja kutsuvat taustalla olevia repository-metodeja.

Näin ohjaimet pysyvät selkeinä: ne keskittyvät HTTP-pyyntöjen käsittelyyn ja delegoivat tietokantaoperaatiot palvelukerrokselle.

// products.service.ts (simplified NestJS service)
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { Product } from './product.entity'; // Your entity

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
  ) {}

  async create(productData: Partial<Product>): Promise<Product> {
    const newProduct = this.productsRepository.create(productData);
    return this.productsRepository.save(newProduct);
  }

  async findAll(): Promise<Product[]> {
    return this.productsRepository.find();
  }

  async findOne(id: number): Promise<Product | undefined> {
    return this.productsRepository.findOneBy({ id });
  }

  async update(id: number, productData: Partial<Product>): Promise<Product | undefined> {
    await this.productsRepository.update(id, productData);
    return this.findOne(id); 
  }

  async remove(id: number): Promise<void> {
    await this.productsRepository.delete(id);
  }
}

Vankka CRUD: parhaat käytännöt

Vankkojen CRUD-toimintojen rakentaminen edellyttää muutakin kuin repository-metodien kutsumista:

  • Tietojen validointi: Käyttäkää Data Transfer Objecteja (DTO) ja validointiputkia varmistaaksenne saapuvien tietojen kelvollisuuden (käsitellään osiossa A2 L2).
  • Virheenkäsittely: Käsitelkää hallitusti esimerkiksi tilanne, jossa entiteettiä ei löydy, heittämällä asianmukaisia poikkeuksia (kuten NestJS:n NotFoundException).
  • Transaktiot: Käyttäkää useita tietokantamuutoksia sisältävissä monimutkaisissa toiminnoissa transaktioita (käsitellään osiossa C1 L2) tietojen yhdenmukaisuuden varmistamiseksi.

CRUD-toimintojen tietovisa

Mitä TypeORM-repositoryn metodia käytetään tyypillisesti vain tiettyjen entiteetin kenttien päivittämiseen ilman, että koko entiteetti ladataan ensin muistiin?

Kertaus: CRUD-toimintojen hallinta

Hienoa työtä! Olet oppinut TypeORM-repositoryjen CRUD-toimintojen perusteet.

  • Luo: Lisää uusia entiteettejä käyttämällä .save()-metodia.
  • Lue: Hae entiteettejä käyttämällä metodeja .find() ja .findOneBy().
  • Päivitä: Muokkaa entiteettiä ja käytä .save()-metodia tai käytä osittaisiin päivityksiin .update()-metodia.
  • Poista: Poista entiteettejä käyttämällä metodeja .delete() tai .remove().

Nämä taidot ovat olennaisia rakennettaessa tietopohjaisia API-rajapintoja NestJS:llä ja TypeORMilla. Seuraavaksi tutustumme virheenkäsittelyyn ja interceptoreihin, joiden avulla API-rajapinnoista tehdään entistä vankempia!

Aloita maksutta

Opi TypeScript tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
20
Oppitunnit
76

Usein kysytyt kysymykset

Onko oppitunti ”CRUD-operaatiot TypeORMilla” ilmainen?

Kyllä – oppitunnin ”CRUD-operaatiot TypeORMilla” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko NestJS-yritysbackendien API:t-kurssin, päivitä CoddyKit PROhon. NestJS-yritysbackendien API:t-kurssilla on yhteensä 3 oppituntia.

Mitä opin oppitunnilla ”CRUD-operaatiot TypeORMilla”?

Kehittäkää entiteeteille täydet Create-, Read-, Update- ja Delete-toiminnot TypeORM-repositoryjen avulla. Harjoittelet NestJS-yritysbackendien API:t-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni NestJS-yritysbackendien API:t-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin NestJS-yritysbackendien API:t-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/3.

Kuinka kauan ”CRUD-operaatiot TypeORMilla”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä NestJS-yritysbackendien API:t-oppitunnilla?

Kyllä. Jokainen NestJS-yritysbackendien API:t-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Reitit ja pyyntöjen käsittely
  2. CRUD-operaatiot TypeORMilla
  3. Virheenkäsittely ja interceptorit
← Takaisin: NestJS-yritysbackendien API:t