Základní operace CRUD v Prismě

Vítejte zpět v seriálu Playing with Prisma!

V předchozím článku jsme se podívali na to, jak nastavit jednoduchou aplikaci TypeScript a zprovoznit Prisma.

Na konci tutoriálu jsme měli jednoduchou aplikaci, která po spuštění vytiskla některá data. To je docela fajn, ale myslím, že bychom měli prozkoumat víc!

V tomto článku rozšíříme naši předchozí aplikaci, abychom mohli využívat každou z hlavních funkcí CRUD-y Prismy a zároveň se dozvěděli, jak psaní pomocí Prismy pomáhá zajistit bezpečnost našeho kódu a snazší vývoj.

Co je CRUD

Pokud ještě nevíte, CRUD je zkratka pro Create, Read, Update, Delete. Prisma poskytuje sadu nástrojů, kterou budeme potřebovat k provádění těchto operací s našimi daty, prostřednictvím sady intuitivních funkcí.

Tyto funkce jsou:

  • Vytvořit:create , createMany
  • Přečtěte si:findFirst , findMany , findUnique
  • Aktualizace:update , updateMany , upsert
  • Smazat:delete , deleteMany

Na každý z nich se zde krátce podíváme, abychom zjistili, jak je používat.

Začneme se také zabývat typy TypeScript, které jsou generovány pro každou z funkcí, abychom si usnadnili život!

Kde začínáme

Abychom stručně zrekapitulovali stav naší aplikace vytvořené v předchozím článku této série, podívejte se na následující část kódu.

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
    const users = await prisma.user.findMany();
    console.log(JSON.stringify(users));
}

main()
    .catch( e => { throw e })
    .finally( async () => await prisma.$disconnect() )

Zde se připojujeme k databázi SQLite pomocí Prisma Client který byl vygenerován ze schématu, které jsme dali dohromady a které aktuálně obsahuje User Modelka.

Neudělali jsme však nic jiného než toto a údaje v našem User tabulka byla přidána ručně přes Prisma Studio.

Myslím, že dobrým prvním krokem k lepšímu pochopení možností CRUD společnosti Prisma je naučit se vytvářet záznamy v našem User tabulky prostřednictvím kódu spíše než uživatelského rozhraní Prisma Studio!

Vytváření dat

Prisma poskytuje dvě hlavní funkce, které umožňují vytvářet data. Jedná se o create a createMany .

create()

Vytváření dat je s create velmi jednoduché funkce.

Tato funkce přijímá objekt s data klíč a select klíč. Vrací Promise s User objekt.

  • data:Objekt typu Prisma.UserCreateInput
  • vyberte:Objekt typu Prisma.UserSelect

Tato funkce standardně vrací nově vytvořený záznam a volitelný select klíč umožňuje definovat, která pole chcete vrátit, pokud nechcete celý objekt.

Udělejte si chvilku a přečtěte si tento blok kódu.

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()

async function main() {
    const newUser = await prisma.user.create({
        data: {
            firstName: 'Sabin',
            lastName: 'Adams',
            email: '[email protected]'
        },
        select: {
           id: true,
           email: true
        }
    })

    console.log(newUser);
}

main()
    .catch( e => { throw e })
    .finally( async () => await prisma.$disconnect() )

Kód vytvoří nový uživatelský záznam a vrátí nově vygenerovaný e-mail a ID uživatele.

To je skvělé, pokud již znáte očekávaný tvar vašeho User model, ale co když si nejste jisti, co je to User vypadá nebo pracujete ve větším týmu lidí, kteří možná nevědí, které obory jsou povinné a které ne?

Když je vygenerován Prisma Client, Prisma automaticky vygeneruje typy TypeScript pro modely, které nastavíte, a očekávané vstupy pro různé funkce, jako jsou dostupná pole při vytváření User .

Tyto typy můžeme importovat a používat je, abychom věděli, které klíče jsou k dispozici a které jsou vyžadovány.

Abychom to uvedli do praxe, napíšeme createUser funkce.

import { PrismaClient, Prisma, User } from '@prisma/client'

const prisma = new PrismaClient()

async function createUser( 
    data: Prisma.UserCreateInput
): Promise<User> {
    return await prisma.user.create({ 
        data,
        select: {
            id: true,
            email: true
        }
     })
}

Funkce zabírá data parametr, který musí být typu Prisma.UserCreateInput a vrátí Promise, který poskytuje User při vyřešení. Ty vyrobila Prisma na základě vašich modelů.

Pokud tento kód spustíte a poskytnete mu nějaká data, můžete prostřednictvím Prisma Studia vidět, že do vaší databáze byl přidán nový uživatel!

To je mnohem hezčí! Můžeme to ale ještě vylepšit. Váš editor kódu si pravděpodobně stěžuje na návratovou hodnotu této funkce, protože neodpovídá User model (vybíráme pole id a email).

Abychom to napravili, řekněme TypeScriptu, že naše funkce vrátí pouze část User model, nazývaný také Partial což je typ nástroje TypeScript.

async function createUser( 
    data: User.UserCreateInput
): Promise<Partial<User>> {...}

Pěkný! Pokud ještě nevidíte hodnotu typů, začněte používat createUser funkce ve vašem main() a podívejte se, co IntelliSense dělá:

Jako vývojář to dělá věci super pěknými. Nyní si můžeme být poměrně jisti, že každý, kdo tuto funkci používá, bude vědět, co by měl dostat se k této funkci a co očekávat zpět, protože jejich IntelliSense zobrazí pole a bude si stěžovat, pokud mu poskytneme nesprávná data.

createMany()

Co se stane, když chceme vytvořit hromadu záznamů najednou? Nechceme, abychom museli spouštět spoustu samostatných dotazů, abychom dostali tato data do databáze. To je přesně to, co createMany je pro.

Tato funkce je podobná funkci create funkce. Rozdíl je v tom, že jeho data klíč přijímá pole objektů odpovídajících typu modelu namísto jednoho objektu.

  • data:Prisma.UserCreateManyInput[]
  • skipDuplicates:Boolean

Také nevrací záznamy, které generuje. Místo toho vrací počet vytvořených záznamů, který se řídí BatchPayload Prismy zadejte a vypadá nějak takto:

{ "count": 3 }

Zde je příklad:

async function createUsers( 
    data: Prisma.UserCreateManyInput[]
): Promise<Prisma.BatchPayload> {
    return await prisma.user.createMany({ data })
}

const results = await createUsers([
    {
        email: '[email protected]',
        firstName: 'Sabin',
        lastName: 'Adams'
    },
    {
        email: '[email protected]',
        firstName: 'Jimmy',
        lastName: 'John'
    }
]);

// result = { count: 2 }

createMany funkce také přijímá klíč skipDuplicates což může být true nebo false . Pokud je nastaveno na hodnotu true, nevytvoří duplicitní řádky.

await prisma.user.createMany({
   data: [ ... your data ],
   skipDuplicates: true
})

Čtení dat

Stejně jako funkce Create je čtení dat s Prisma velmi intuitivní. Určitě existuje několik složitých dotazů a dotazů včetně vztahů, které lze vytvořit, ale zde si projdeme některé základy.

findFirst()

V případě, že byste chtěli ve své tabulce najít pouze jeden záznam, který odpovídá zadaným kritériím, můžete použít findFirst .

Tato funkce přijímá objekt, který jí říká parametry, které má použít při prohledávání tabulky. Poté vrátí první odpovídající záznam, který najde.

Podívejme se na příklad.

import { PrismaClient, Prisma } from '@prisma/client'
const prisma = new PrismaClient()
const oldUser = await prisma.user.findFirst({
    where: {
        age: {
            gt: 20
        }
    }
})

Také jsem přidal volitelný age pole na náš User model v databázovém schématu. Zde je aktualizovaný model v schema.prisma

model User {
  id        Int    @id @default(autoincrement())
  firstName String
  lastName  String
  email     String
  age       Int?
}

Tento dotaz vyhledá všechny uživatele starší 20 let a vrátí prvního, který odpovídá. Dostanete zpět User z funkce.

Kromě where můžete poskytnout i další možnosti pro filtrování dat a konfiguraci toho, co chcete z funkce vrátit.

Řekněme například, že chceme NEJSTARŠÍHO uživatele, který je mladší 20 let. Nejprve bychom mohli seřadit tabulku podle age před výběrem prvního shodného záznamu pomocí orderBy volba.

const oldestUserBelowTwenty = await prisma.user.findFirst({
   orderBy: {
       age: 'desc'
   },
    where: {
        age: {
            lt: 20
        }
    }
})

Tím budou uživatelé filtrováni pouze na uživatele starší 20 let. PAK tento výběr seřadí od nejstaršího po nejmladší. Pokud z toho seženeme první záznam, našli jsme nejstaršího geezera v naší databázi!

findMany()

Tato funkce je velmi podobná funkci findUnique , kromě toho, že místo zachycení prvního shodného záznamu vrátí VŠECHNY odpovídající záznamy.

Pokud bychom chtěli seznam ID uživatelů, kteří jsou starší 20 let, seřazený podle příjmení A-Z, mohli bychom napsat:

const usersOverTwenty = await prisma.user.findMany({
    select: {
        id: true
    },
    orderBy: {
        lastName: 'asc'
    },
    where: {
        age: {
            gt: 20
        }
    }
})

findUnique()

Tato funkce, jako findFirst funkce, vrátí jeden záznam. Rozdíl je však v tom, že where filtr umožňuje vyhledávat pouze v polích, která jsou jedinečná .

Vzpomeňte si na naše User Modelka.

model User {
  id        Int    @id @default(autoincrement())
  firstName String
  lastName  String
  email     String
  age       Int?
}

@id pole jsou příklady jedinečných polí. Níže je uveden úplný seznam atributů, které znamenají, že pole je jedinečné .

  • @id :Atribut označující primární klíč vaší tabulky (pokud takový existuje)
  • @@id :Identifikátor sloučeniny
  • @unique :Atribut označující pole, které by mělo být jedinečné
  • @@unique :Jedinečný identifikátor

Každou z nich a mnohé další si projdeme v dalším článku této série, který se ponoří hluboko do různých možností, které máme při sestavování našich schémat.

Podívejte se na tento příklad findUnique funkce v akci

const user = await prisma.user.findUnique({
    where: {
        id: 3
    }
})

Tím získáte User podle jeho jedinečného identifikátoru id . Chceme, aby uživatel měl id ze 3. Při vypisování filtru si všimnete, že váš IntelliSense neuvádí všechna pole modelu jako dostupné možnosti. Pouze umožňuje filtrovat podle jedinečného pole v našem modelu.

Není to docela sladké?

Tato funkce také umožňuje select která pole zahrnout do vráceného objektu a mnoho dalších možností, stejně jako ostatní funkce „čtení“.

Nyní, když můžeme číst data, přejděme k aktualizaci!

Aktualizace dat

Prisma má spoustu skvělých funkcí, které nám umožňují aktualizovat naše data různými způsoby. Můžeme aktualizovat jeden nebo více záznamů, nebo dokonce zvolit vytvoření záznamu, pokud nebyl nalezen žádný odpovídající záznam k aktualizaci pomocí upsert .

Níže si projdeme základy každé z těchto funkcí.

update()

update funkce nám umožňuje aktualizovat jeden záznam. Můžete určit, který záznam se má aktualizovat, stejným způsobem jako findUnique funkce určuje, který záznam se má uchopit. Svůj dotaz zakládá pouze na unikátních identifikátory tohoto modelu.

const user = await prisma.user.update({
    select: {
        id: true,
        age: true
    },
    where: {
        id: 4
    },
    data: {
        age: 7
    }
})

Výše uvedený dotaz aktualizuje jednoho uživatele, jehož id je 4. Nastaví age tohoto uživatele na 7 a vrátí objekt obsahující id a age pole.

Pokud jsme neposkytli select klíč, celý User předmět by byl vrácen.

Jednou skvělý trik, o kterém bych se rád zmínil, je ten, že pokud aktualizujete numerický pole, existuje několik pomocných možností, které můžeme použít k provádění matematických operací s naším číslem a ukládání výsledků. Toto jsou dostupné možnosti.

Možnost Popis
přírůstek Přidá zadanou hodnotu ke stávající hodnotě
snížit Odečte zadané číslo hodnoty od existující hodnoty
násobit Vynásobí existující hodnotu zadanou hodnotou
rozdělit Vydělí existující hodnotu zadanou hodnotou

Řekněme, že například uplynuly narozeniny našeho uživatele a my potřebujeme zvýšit jeho age v databázi. Pomocí následujícího dotazu bychom mohli zvýšit její věk o 1 a získat zpět záznam uživatele.

const user = await prisma.user.update({
    where: {
        id: 4
    },
    data: {
        age: {
            increment: 1
        }
    }
})

updateMany()

Máme také možnost aktualizovat mnoho záznamů najednou pomocí updateMany funkce. Vstup této funkce má následující možnosti:

  • where :Vyhledávací dotaz k nalezení záznamů k aktualizaci
  • data :Aktualizace, které chceme provést

To, co dostaneme zpět, se liší od update funkce však. Podobné jako createMany místo toho dostaneme zpět Prisma.BatchPayload objekt, který má níže uvedený formát. count klíč je počet záznamů, které skutečně obdržely aktualizace.

{ "count": number }

Abychom to uvedli do praxe, aktualizujme všechny uživatele, kteří mají lastName Adams a Williams mít stejnou e-mailovou adresu.

const results = await prisma.user.updateMany({
    where: {
        lastName: {
            in: ['Adams', 'Williams']
        }
    },
    data: {
        email: '[email protected]'
    }
})

// results could be { "count": 5 } if five records were updated

K dotazu na správné uživatele jsem použil jiný operátor dostupný v našem where výpisy:in . To nám umožňuje předat pole hodnot, s nimiž se pole shoduje.

Snadné věci! Je tak hezké, jak nám Prisma umožňuje skládat dotazy a operace způsobem, který je tak intuitivní. Přejděme k poslední funkci aktualizace.

upsert()

Upsert je speciální aktualizace. Nebyl jsem si jistý, jestli to mám nalepit do sekce Vytvořit nebo sem s Aktualizacemi. Důvodem je, že dělá obojí v závislosti na tom, co najde v databázi!

Pokud neznáte terminologii databáze, upsert je aktualizace, která, pokud nenajde žádné záznamy k aktualizaci odpovídající kritériím vyhledávání, vytvoří místo toho záznam.

Řekněme, že máme nějaká uživatelská data. Chceme aktualizovat uživatele s těmito daty, ale pokud se to týká nového uživatele, který ještě v databázi neexistuje, měli by místo toho vytvořit uživatele s novými daty. To je přesně to, co upsert je pro!

Pokud to nedává smysl, podívejte se na tuto ukázku.

const user: User = {
    id: 3
    firstName: 'Johnny',
    lastName: 'Bravo',
    email: '[email protected]',
    age: 25
}

const user = await prisma.user.upsert({
    select: { id: true },
    where: {
        id: userId
    },
    update: {
        age: user.age
    },
    create: user
})

Dobře, co se tady děje?

Začneme s nějakým user podrobnosti. Můžeme předstírat, že někdo provedl nějaké změny ve svém účtu, a kliknout na Uložit. Toto jsou hodnoty, které byly odeslány na náš server.

Poté přes Prisma spustíme dotaz, který vyhledá uživatele s daným id a aktualizujte věkovou hodnotu tohoto záznamu.

Co když to byl uživatel vytvářející nový účet a toto je obecný saveUser koncový bod, který zpracovává vytváření nových uživatelů a aktualizaci stávajících? S naším upsert můžeme určit, že pokud nebyly provedeny žádné shody, pomocí where klauzule bychom měli spustit create místo toho s některými daty (v tomto případě celý user datový objekt, který byl odeslán).

A nakonec prostřednictvím select možnost, kterou jsme zadali, že chceme získat ID aktualizovaného (nebo vytvořeného) uživatele zpět. Pokud by toto bylo vynecháno, dostali bychom celý User objekt.

To jsou hlavní funkce související s aktualizací! Doposud jsme prošli hromadou obsahu a jsme blízko k tomu, abychom zabalili přehled našich funkcí CRUD, takže čest, že jste tam se mnou zatím vydrželi!

Přejděte k funkcím mazání!

Mazání dat

Poslední kousek CRUD (hah) podíváme se na funkce Delete. Tyto funkce jsou docela jednoduché a přímočaré, takže pojďme rovnou do toho.

delete()

delete Funkce dělá přesně to, co zní, jako by dělala, smaže záznam dat ve vaší tabulce.

Ve svém vstupu vyžaduje dvě možnosti:

  • where :Možnost kde filtruje jedinečné pouze pole
  • select :Umožňuje vybrat data ze záznamu, který odstraňujete
const deleted = await prisma.user.delete({
    where: {
        id: 3
    },
    select: {
        email: true
    }
})

V tomto dotazu odstraňujeme User s id ze 3. Také vracíme email tohoto uživatele, aby s ním následně provedl nějaké následné zpracování (nebo něco takového...).

Pokud ponecháme select prázdné, dostali bychom zpět celý User záznam, který jsme smazali.

deleteMany()

Poslední funkcí, na kterou se v tomto článku podíváme, je deleteMany . Toto je docela jednoduché a řídí se mnoha konvencemi, které jsme viděli výše.

Tato funkce přijímá vstup s where volba. To nám umožňuje filtrovat záznamy, které chceme smazat.

To, co dostaneme zpět, je Prisma.BatchPayload objekt obsahující počet záznamů, které byly odstraněny.

Tady je v akci:

const results = await prisma.user.deleteMany({
    where: {
        id: {
            notIn: [1,3,4]
        },
        email: {
            endsWith: '@gmail.com'
        }
    }
})

Výše uvedený dotaz odstraní všechny uživatele, jejichž id s nejsou jedna, tři nebo čtyři. Záznam také odstraní, pouze pokud má @gmail.com e-mail.

A je to, docela jednoduché!

Sbalení

To je spousta věcí, které musíte projít! Skvělá práce po dlouhé době a doufám, že jste dokázali něco vyčíst z přehledů a příkladů výše.

To samozřejmě jen poškrábe povrch toho, co Prisma umí a složitost dotazů, které umožňuje. Dotkli jsme se několika skvělých možností filtrování, aktualizace a vkládání, které jsou dostupné prostřednictvím Prisma Client , ale v budoucích článcích se určitě budeme zabývat hlouběji.

Zatím moc děkuji za přečtení! Pokud jste zvědaví a rádi byste se dozvěděli více o funkcionalitě CRUD, kterou Prisma nabízí, nebo o jakékoli jiné operaci CRUD, podívejte se na dokumentaci, kterou Prisma poskytuje.

Hodně štěstí při kódování!