Strona główna / Artykuły

Surowy SQL, Prisma lub Drizzle: wybór warstwy bazy danych dla bezpieczeństwa typów i kontroli

W jaki sposób surowy SQL, Prisma i Drizzle różnią się pod względem kontroli, bezpieczeństwa typów oraz doświadczenia programisty przy pracy z PostgreSQL, oraz praktyczny sposób wyboru jednego z nich do aplikacji typu TypeScript.

Wydawca: REACTAPP.TOP15 min czytania3403 słów

Źródła: Prisma vs Drizzle vs Raw SQL: What Every Backend Developer Should Understand Before Choosing an ORM

Większość prac związanych z warstwą backend rozpoczyna się od uzyskania możliwości otrzymywania odpowiedzi od API. Konfiguruje się ścieżki, dodaje jeden lub dwa kontrolery, wdraża mechanizmy autoryzacji oraz niektóre komponenty middleware, i przez jakiś czas to jest cały obraz sytuacji. Następnie pojawia się trudniejsze pytanie: w jaki sposób aplikacja powinna faktycznie komunikować się ze swoją bazą danych? Nagle pojawia się mnóstwo terminów (surowy SQL, sterowniki, ORM-y, narzędzia do budowania zapytań, Prisma, Drizzle) oraz różne, często sprzeczne opinie. Niektórzy inżynierowie w ogóle nie chcą korzystać z ORM-ów. Inni twierdzą, że Prisma zaoszczędziło im tygodnie pracy. Trzecia grupa wybiera Drizzle, ponieważ pozostawia SQL widoczny zamiast go ukrywać. Poniższe rozdziały omawiają ten temat, wychodząc od podstaw, dzięki czemu wybór między surowym SQL, Prismą a Drizzle w środowisku TypeScript i PostgreSQL staje się decyzją inżynieryjną dotyczącą kontroli i bezpieczeństwa typów, a nie kwestią mody.

Jak żądanie dociera do bazy danych

Zanim nazwa jakiegokolwiek narzędzia nabierze znaczenia, pomocne jest zrozumienie, w jaki sposób żądanie przemieszcza się przez typowy backend. Ktoś odwiedza twoją stronę i naciska przycisk typu „pokaż mój profil”. Ta akcja rozpoczyna się w przeglądarce, przechodzi przez twoją API, do warstwy bazy danych, a na końcu do samej bazy danych, zanim odpowiedź wraca z powrotem.

Browser
   |
   vExpress API   |
   vDatabase Layer   |
   vPostgreSQL Database   |
   vResponse back to the user

Słownik bazy danych znajdujący się pośrodku jest miejscem, gdzie skupia się zamieszanie. Twoja aplikacja potrzebuje mechanizmu do komunikacji z bazą danych, a istnieją trzy powszechne rozwiązania: ręczne pisanie surowego SQL, używanie ORM takiego jak Prisma lub wykorzystywanie narzędzia do budowania zapytań, takiego jak Drizzle. Każdy z poniższych rozdziałów omawia kolejno jedno z tych rozwiązań.

Ręczne pisanie SQL

Najprostsza droga to jednocześnie najstarsza: sam piszesz SQL. Wyobraź sobie prostą tabelę użytkowników z typowymi kolumnami.

users
id
name
email
password

Pobranie pojedynczego użytkownika jest wtedy niezwykle proste.

SELECT *
FROM users
WHERE id = 1;

Ponieważ baza danych używa SQL jako swojego języka naturalnego, nic nie jest tłumaczone podczas wprowadzania ani wycofywania danych. W Node.js z PostgreSQL pakiet pg jest standardowym sposobem na bezpośrednie wysyłanie zapytań. Z niego importuje się zbiór połączeń.

import { Pool } from "pg";

Następnie konfiguruje się zbiór na podstawie zmiennej środowiskowej i wykonywa się wobec niego zapytanie z parametrami, czytając uzyskane w ten sposób wiersze.

const pool = new Pool({
  connectionString: process.env.DATABASE_URL
});const result = await pool.query(
  "SELECT * FROM users WHERE id = $1",
  [1]
);console.log(result.rows);

Twój kod wysyła zapytanie SQL, PostgreSQL je wykonuje, a wiersze wracają bezpośrednio, bez żadnych pośrednich transformacji. Samodzielne pisanie zapytań SQL daje ci pełną kontrolę. Złożone raportowanie, na przykład, jest realizowane bezpośrednio – tak jak łączenie użytkowników z ich zamówieniami oraz grupowanie ich w celu policzenia liczby zamówień każdego użytkownika.

SELECT
users.name,
COUNT(orders.id)
FROM users
JOIN orders
ON users.id = orders.user_id
GROUP BY users.name;

Dla tak skomplikowanych zapytań analitycznych ręcznie pisany SQL jest naprawdę trudny do udoskonalenia. Jego pisanie zmusza również do zrozumienia połączeń tabel, indeksów, planów wykonywania oraz ogólnych aspektów wydajności, a ta wiedza pozostaje cenna niezależnie od tego, jakie narzędzie zostanie później wybrane. Ponadto nie ma żadnej dodatkowej warstwy pomiędzy kodem a silnikiem.

Wady stają się widoczne, gdy projekt przekracza liczbę kilku zapytań. Wyobraź sobie bazę kodu zawierającą setki takich zapytań rozrzuconych w różnych miejscach, w tym wiele przypominających to wyszukiwanie według adresu e-mail.

const result = await pool.query(
`
SELECT *
FROM users
WHERE email=$1
`,
[email]
);

Załóżmy teraz, że ktoś zmienia nazwę kolumny, zamieniając email na email_address. Każde zapytanie korzystające z tej kolumny przestaje działać bez żadnych objawów. Baza danych natychmiast rejestruje tę zmianę, ale Twój kod w TypeScript nie zdaje sobie z tego sprawy, dopóki coś nie pójdzie nie tak w czasie wykonywania. Istnieje subtelniejsza wersja tego samego zagrożenia. Powiedzmy, że pobierasz pierwszy wiersz.

const user = result.rows[0];

Następnie odczytuje się z niego wartość właściwości, z błędem pisowni.

console.log(user.emial);

Błędnie wpisane emial nie powoduje żadnych komunikatów błędu ze strony JavaScript. Dowiadujesz się o tym dopiero wtedy, gdy ta linia kodu zostanie wykonywana i powoduje problemy u użytkownika. Właśnie z powodu takich trudności istnieją ORM-y.

Czym naprawdę jest ORM

ORM to skrót od Object Relational Mapping, co brzmi bardziej imponująco, niż w rzeczywistości jest. Oznacza to po prostu istnienie mostu łączącego język programowania z bazą danych. Zamiast ręcznie pisać taki zapytanie,

SELECT *
FROM users
WHERE id=1;

piszesz coś bardziej przypominającego wywołanie metody w obiekcie.

user.findUnique({
 id: 1
})

ORM bierze to i w tle generuje rzeczywisty SQL, dzięki czemu kod przechodzi przez ORM do wygenerowanej zapytania, a następnie do bazy danych.

Your TypeScript code
        |
        vORM        |
        vSQL query        |
        vDatabase

Zespoły sięgają po ORM, ponieważ nowoczesne aplikacje wymagają czegoś więcej niż tylko sposobu na wysyłanie zapytań. Potrzebują bezpieczeństwa typów, prawdziwych migracji, zarządzania schematem, które nie polega na zapamiętywaniu każdej tabeli, oraz znacznie mniej powtarzalnego kodu szablonowego. W dzisiejszym świecie TypeScript najczęściej wymieniane nazwy to Prisma, Drizzle, TypeORM i Sequelize. Tutaj skupiamy się na surowym SQL, Prismie i Drizzle, ponieważ te trzy rozwiązania obejmują to, co faktycznie biorą pod uwagę większość nowoczesnych backendów opartych na TypeScript.

Poznawanie Prismy

Prisma należy do najczęściej omawianych ORM w ekosystemie TypeScript i najczęściej jest pierwszym nazwiskiem wymienianym w rozmowach na temat nowoczesnych backendów Node.js. Jego istotą jest umożliwienie aplikacji napisanej w TypeScript komunikacji z bazą danych za pomocą zwykłego TypeScript, a nie rozrzuconych wszędzie zapytań SQL; obsługuje on PostgreSQL, MySQL, SQLite, SQL Server oraz MongoDB. Piszesz kod w TypeScript, a Prisma przekształca go w zapytania do bazy danych.

TypeScript code
        |
        vPrisma Client        |
        vSQL queries        |
        vDatabase

Klasyczna architektura Express, Prisma i PostgreSQL jest dobrze zorganizowana: żądania trafiają przez API, przechodzą przez warstwę usług, a następnie docierają do bazy danych za pośrednictwem Prisma Client.

Client
  |
  vExpress API  |
  vService Layer  |
  vPrisma Client  |
  vPostgreSQL Database

Prisma Client to element, który wykonywać rzeczywistą pracę: faktyczny most pomiędzy kodem backendu a bazą danych za nim. Wszystko zaczyna się od jednego pliku, schema.prisma, który opisuje całą strukturę bazy danych w jednym miejscu. Model użytkownika, na przykład, definiuje cztery kolumny o określonym typie.

model User {
  id       Int    @id @default(autoincrement())
  name     String
  email    String @unique
  password String
}

Ten blok mówi Prismie, aby stworzyło tabelę User z tymi kolumnami, a Prisma dokładnie rozumie, co to oznacza. Podczas gdy w przypadku surowego SQL potrzebna byłaby wyraźna instrukcja do utworzenia tej samej tabeli,

CREATE TABLE users (
 id SERIAL PRIMARY KEY,
 name TEXT,
 email TEXT UNIQUE,
 password TEXT
);

Prisma potrzebuje jedynie powyższego bloku schematu i zajmuje się jego przekształceniem w rzeczywiste zmiany w bazie danych za pomocą migracji. Migracja to w istocie zapisana historia zmian w bazie danych na przestrzeni czasu. Załóżmy, że tabela użytkowników jest początkowo minimalna.

User
id
name
email

Kilka miesięcy później chcesz dodać numer telefonu. Zamiast ręcznie edytować tabelę, wykorzystujesz jeden poleceń.

npx prisma migrate dev --name add_phone_number

Prisma tworzy plik migracji, aby odnotować tę zmianę, dzięki czemu folder rozwija się od migracji związanej z początkowym ustawieniem do tej, która dodaje numer telefonu, a cała historia pozostaje wersjonowana, zamiast istnieć tylko w pamięci kogoś. Gdy schemat zostanie zdefiniowany, Prisma generuje klienta, którego można używać w kodzie, na przykład do pobrania użytkownika podanego przez ID.

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

Za kulisami, gdzie to cicho staje się zwykłym SQL-em.

SELECT *
FROM users
WHERE id=1;

Nigdy nie musiałeś sam wpisywać kodu SQL. Codzienne operacje również mapują się w sposób intuicyjny: dodawanie danych staje się wywołaniem funkcji create, pobieranie wszystkich danych — wywołaniem funkcji find-many, aktualizacja rekordu — wywołaniem funkcji update z klauzulą where, a usuwanie — wywołaniem funkcji delete. To właśnie dzięki bezpieczeństwu typów Prisma zyskuje swoją dobrą reputację. Jeśli twoja struktura danych definiuje obiekt User zawierający id, imię i adres e-mail, to podczas wyszukiwania otrzymujesz prawdziwe uzupełnianie tekstu dla każdego pola.

const user = await prisma.user.findMany();
user[0].email;

Błędnie zapisz to jako user[0].emali, a Prisma w połączeniu z TypeScriptem zidentyfikuje ten błąd jeszcze przed uruchomieniem aplikacji, co z czasem pozwala zaoszczędzić sporo czasu na debugowaniu. Oczywiście prawdziwe aplikacje rzadko składają się z jednej tabeli. Załóżmy, że jeden użytkownik posiada wiele wpisów – w takim przypadku mamy tabelę z danymi użytkowników oraz tabelę z danymi wpisów, połączone za pomocą pola user id.

User
id
namePostid
title
userId

Prisma wyraża tę relację bezpośrednio w schemacie, zarówno w modelu użytkownika,

model User {
 id Int @id @default(autoincrement())

a model post, który odwołuje się do swojego użytkownika za pośrednictwem relacji.

 name String posts Post[]
}model Post { id Int @id @default(autoincrement()) title String userId Int user User @relation(
 fields:[userId],
 references:[id]
 )}

Ładowanie użytkownika wraz z jego postami staje się wtedy jedną czytelną instrukcją, która obejmuje powiązane rekordy.

const user = await prisma.user.findUnique({
 where:{
  id:1
 },
 include:{
  posts:true
 }
});

To zwraca obiekt użytkownika z wplecionym tablicą wpisów, dokładnie w takiej formie, jakiej oczekujesz w kodzie aplikacji. Zalety Prismy wynikają z tego, jak naturalne jest doświadczenie programisty, jak wysoka jest jego bezpieczeństwo typów w projektach TypeScript, jak uporządkowane stają się migracje, jak solidna jest dokumentacja oraz jak szybko cały zespół może przeczytać schemat i zrozumieć strukturę bazy danych.

Nie jest wolny od kompromisów. Naprawdę złożone raportowanie może wydawać się niewygodne w porównaniu z ręcznie pisanym SQL. Ponieważ Prisma tak skutecznie ukrywa SQL, programiści, którzy uczą się tylko Prismy, czasami mają trudności później, gdy muszą optymalizować na poziomie bazy danych. Technicznie w ścieżce żądania istnieje jeszcze jeden etap – od aplikacji przez Prismę do SQL, a potem do bazy danych – zamiast krótszej ścieżki od aplikacji bezpośrednio do SQL, a następnie do bazy danych. Dla większości aplikacji ten dodatkowy warstwa nie stanowi problemu, ale warto wiedzieć, że istnieje. Prisma najlepiej sprawdza się, gdy buduje się API w TypeScript, chce się szybkiego rozwoju, głównie potrzebne są standardowe operacje create-read-update-delete, a także bardzo zależy się na doświadczeniu programisty – dlatego wiele startupów i produktów SaaS polega na nim.

Gdzie pasuje Drizzle

Drizzle to kolejny ORM, który zyskał realny rozgłos w TypeScript, a zrozumienie go zaczyna się od uświadomienia sobie, że Prisma i Drizzle opierają się na zupełnie różnych filozofiach. Prisma stara się jak najbardziej chronić programistów przed złożonością bazy danych. Drizzle przyjmuje przeciwną postawę: trzyma programistów blisko SQL i po prostu dodaje na wierzch bezpieczeństwo zapewniane przez TypeScript. Obie metody dążą do tego samego celu – lepszej obsługi bazy danych – ale różnymi drogami. Drizzle to lekki ORM dla TypeScript, przeznaczony do PostgreSQL, MySQL i SQLite, skupiony na bezpieczeństwie typów, wydajności, bliskości SQL oraz prostej architekturze. Nadal piszesz kod w TypeScript, który przechodzi przez Drizzle, by stać się zapytaniem skierowanym do bazy danych.

TypeScript code
        |
        vDrizzle ORM        |
        vSQL query        |
        vDatabase

Umieszczenie ich obok siebie uwydatnia filozoficzną luki. Pobieranie wszystkich użytkowników w Prisma odbywa się w sposób opisany poniżej, a SQL, który jest w tym przypadku generowany, rzadko przychodzi nam do głowy.

const user = await prisma.user.findMany();

Drizzle prosi o coś, co bardziej przypomina rzeczywisty SQL.

const users = await db
.select()
.from(usersTable);

Można praktycznie bezpośrednio z kodu odczytać zachowanie na poziomie bazy danych. Schematy również się różnią. Prisma przechowuje je w dedykowanym pliku schema.prisma, natomiast Drizzle deklaruje schemat w zwykłym TypeScript, najpierw importując potrzebne narzędzia do tworzenia kolumn.

import {
 pgTable,
 serial,
 varchar
} from "drizzle-orm/pg-core";

i deklarowanie tabeli z wypełnionymi kolumnami.

export const users = pgTable("users", { id: serial("id").primaryKey(), name: varchar("name"), email: varchar("email")});

Struktura twojej bazy danych znajduje się więc bezpośrednio w kodzie TypeScript, więc nie musisz korzystać z żadnego dodatkowego dialektu schematu. Codzienne operacje przypominają zapytania SQL, tylko w ubraniu TypeScript: operacja wprowadzania danych to wywołanie funkcji insert z przekazywanymi wartościami, odczytanie wszystkich danych to zapytanie select odnoszące się do tabeli, a filtrowanie polega na dodaniu klauzuli where utworzonej za pomocą narzędzia do porównywania wartości. Ludzie wolą Drizzle z kilku powodów. Jest naprawdę lekki i ma niewiele warstw abstrakcji. Ponieważ pozostaje bliski SQL, twoje umiejętności w tym języku stale się doskonalą, zamiast zanikać pod wpływem abstrakcji. Generowane przez niego zapytania są zazwyczaj przewidywalne, ponieważ między twoją intencją a wynikiem znajduje się mniej warstw przetwarzania. Ponadto został zaprojektowany z myślą o nowoczesnych środowiskach wykonawczych, w tym środowiskach typu edge i serverless.

Z drugiej strony, jeśli nigdy naprawdę nie uczyłeś się SQL, Drizzle może na początku wydawać się bardziej mylący niż Prisma, które zazwyczaj jest łatwiejsze do opanowania dla początkujących. Dobrze korzystanie z Drizzle wymaga rzeczywiście zrozumienia połączeń, relacji, indeksów oraz sposobu działania zapytań, bez ukrywania żadnego z tych elementów. I chociaż jego ekosystem, dokumentacja i społeczność są solidne, pozostają one mniejsze w porównaniu z bardziej rozwiniętym otoczeniem Prismy. Najlepszy sposób na przedstawienie tego kontrastu brzmi tak: przy użyciu Prismy traktujemy bazę danych jako zbiór obiektów, a nacisk kładziony jest na doświadczenie programisty, natomiast przy użyciu Drizzle traktujemy bazę danych bardziej jak sam SQL, a nacisk przesuwa się w stronę wydajności i kontroli.

Trzy podejścia obok siebie

Biorąc pod uwagę wszystkie trzy, ich architektury układają się w spektrum pokazujące, ile warstw znajduje się pomiędzy aplikacją a bazą danych.

Raw SQL:      Application → SQL Query → Database
Prisma:       Application → Prisma Client → SQL → Database
Drizzle:      Application → Drizzle → SQL → Database

Rozważmy prostą operację, taką jak wyszukiwanie użytkownika według adresu e-mail w podstawowej tabeli użytkowników. W surowym SQL wysyłamy bezpośrednio zapytanie z parametrami.

const result = await pool.query(
`
SELECT *
FROM users
WHERE email=$1
`,
["rahul@test.com"]
);

W Prisma wywołuje się unikalne wyszukiwanie.

const user =
await prisma.user.findUnique({

przekazywanie adresu e-mail w klauzuli where.

where:{
 <email:"rahul@test.com>"
}});

A w Drizzle tworzysz wybór ograniczony równością w kolumnie adresu e-mail.

const user =
await db
.select()
.from(users)
.where(
 eq(
 users.email,
 "rahul@test.com"
 )
);

W końcu wszystkie trzy narzędzia zwracają te same dane. Różnice polegają na tym, jak duży kontrolę chcesz mieć, jaką poziom abstrakcji jest ci odporny, oraz ile wiesz już o SQL. Powszechnym błędem początkujących jest skupianie się na tym, które narzędzie jest najszybsze, podczas gdy w rzeczywistych projektach szybkość rzadko jest decydującym czynnikiem. Lepsze pytania brzmią: z czym zespół już ma doświadczenie, jak naprawdę złożony jest projekt, jaki poziom wiedzy o bazach danych ma każdy, oraz jak skomplikowane mogą stać się zapytania. Prosta aplikacja typu SaaS zazwyczaj funkcjonuje dobrze przy użyciu samego Prismy, natomiast w aplikacjach wymagających intensywnego korzystania z bazy danych Drizzle lub surowy SQL w niektórych miejscach okazują się przydatne.

Podsumowując zalety i wady w prostych słowach: krzywa uczenia się jest najtrudniejsza przy użyciu surowego SQL, najłatwiejsza przy Prismie, a umiarkowana przy Drizzle. Kontrola nad SQL jest najwyższa przy surowym SQL, wysoka przy Drizzle, a najniższa przy Prismie. Bezpieczeństwo typów jest słabe przy surowym SQL, ale doskonałe zarówno przy Prismie, jak i przy Drizzle. Wydajność surowego kodu jest doskonała przy surowym SQL i Drizzle, a dobra przy Prismie. Doświadczenie programisty jest najlepsze przy Prismie, dobre przy Drizzle, a przeciętne przy surowym SQL. Złożone zapytania lepiej radzą sobie z surowym SQL i Drizzle, natomiast Prisma plasuje się pośrodku. Jeśli chodzi o łatwość używania dla początkujących, Prisma prowadzi, Drizzle znajduje się pośrodku, a surowy SQL jest w tyle.

Więc co właściwie powinieneś wybrać?

Czy ORM powinno zawsze być narzędziem domyślnym? Szczerze mówiąc, nie. Podobnie jak każde inne narzędzie, ORM nadaje się do niektórych zadań, a nie do innych, i nic nie pasuje do każdego projektu. Surowy SQL znajduje zastosowanie wtedy, gdy potrzebujesz maksymalnej kontroli, gdy aplikacja jest naprawdę intensywnie wykorzystywana przez bazę danych, gdy zapytania stają się złożone lub gdy wydajność absolutnie nie może spaść. Pasują do tego platformy analityczne, systemy raportowe oraz aplikacje finansowe, a także wszystko to, co przetwarza duże ilości danych. Raport odpowiadający na pytanie o to, ile każdy klient kupił w ciągu pięciu lat wraz z ich średnią wartością zamówienia, naturalnie łatwiej jest sformułować bezpośrednio w SQL, wybierając odpowiednie funkcje agregacyjne.

SELECT
customers.name,
COUNT(orders.id),
AVG(orders.amount)

i łączenie oraz grupowanie w celu ich stworzenia.

FROM customersJOIN ordersON customers.id = orders.customer_idGROUP BY customers.name;

Dla takich zadań nic tak naprawdę nie może zastąpić tego, co potrafi wyrazić SQL. Problem polega na tym, że poleganie na ręcznie pisanym SQL do wszystkiego w dużym kodzie szybko staje się niewygodne – powstają setki plików z zapytaniami rozrzuconych tu i ówdzie, a bezpieczeństwo typów wymaga ciągłej kontroli ze strony użytkownika. Przegląd wydajności API Node.js uporządkowany według priorytetów pokazuje, jak często to baza danych, a nie framework, decyduje o rzeczywistych korzyściach i stratach.

Prisma jest zazwyczaj lepszym wyborem, gdy budujesz backend w TypeScript, zajmując się głównie standardowymi operacjami tworzenia, odczytu, aktualizacji i usuwania danych, ceniąc szybkość rozwoju oraz koordynując pracę kilku programistów w jednym kodzie. Opisuje to produkty typu SaaS, panele administracyjne, API sklepów internetowych oraz systemy zarządzania treścią, gdzie codziennymi zadaniami są tworzenie kont użytkowników, aktualizacja profili, składanie zamówień lub pobieranie danych z paneli. Prisma rzeczywiście zwiększa produktywność, przekształcając proste zapytania w czytelne i przejrzyste funkcje, które każdy kolega z zespołu może szybko zrozumieć. Drizzle nadaje się wtedy, gdy pracujesz z TypeScript, już dobrze znasz SQL, zależy ci na wydajności i kontroli, a chcesz coś lekkiego zamiast ciężkiego. Przyciąga ono programistów, którzy chcą bezpieczeństwa oferowanego przez ORM, nie chcąc jednocześnie być zbyt daleko oddalonymi od bazy danych.

W praktyce wiele rzeczywistych projektów nie trzyma się jednego podejścia. Startup może rozsądnie zacząć od prostej ścieżki od Express przez Prisma aż do PostgreSQL.

Express → Prisma → PostgreSQL

Jako pierwszy krok jest to naprawdę rozsądne. Później, gdy produkt się rozwija i pojawiają się bardziej zaawansowane potrzeby analityczne lub raportowe, zespoły często dodają surowe zapytania SQL w konkretnych miejscach, zamiast usunąć Prisma, w rezultacie aplikacja łączy się z PostgreSQL zarówno za pośrednictwem Prisma, jak i ręcznie napisanych zapytań.

Application
├── Prisma
└── Raw SQL Queries
        ↓
   PostgreSQL

Stosowanie jednego podejścia wszędzie nie jest regułą, której musisz przestrzegać, a większość rzeczywistych baz kodu w końcu łączy różne metody. Startupy często polegają na TypeScript, Prisma i PostgreSQL wyłącznie ze względu na szybkość. Nowsze projekty napisane w TypeScript coraz częściej wybierają Drizzle ze względu na jego lekki styl przyjazny SQL. Natomiast większe systemy często łączą ORM z surowym SQL oraz specjalistyczną optymalizacją bazy danych, po prostu dlatego, że różne części dużego systemu napotykają różne problemy.

Błędy, których warto unikać

Często powtarzają się pewne błędy. Pierwszym z nich jest traktowanie ORM tak, jakby było bazą danych. Prisma nie jest bazą danych, podobnie jak Drizzle; są to narzędzia służące do dostępu do bazy danych, niezależnie od tego, czy jest to PostgreSQL, MySQL, czy MongoDB. Drugim błędem jest całkowite pomijanie SQL na rzecz bezpośredniego korzystania z Prismy. Na początku może to wydawać się efektywne, ale zwykle skutkuje problemami, gdy powolne zapytania stają się trudne do wyjaśnienia, połączenia wydają się niepotrzebnie skomplikowane, a optymalizacja staje się naprawdę trudna z powodu braku solidnych podstaw pod abstrakcją. ORM nigdy nie miało zastąpić SQL – istnieje nad nim. Trzecim błędem jest dążenie przede wszystkim do maksymalnych wyników wydajności. Produktywność programistów ma takie samo znaczenie w rzeczywistym świecie, a oszczędzenie pięciu milisekund na zapytaniu kosztem trzech miesięcy wolniejszego rozwoju rzadko jest mądrą decyzją.

Ścieżka nauki, która się opłaca

Zaczynając od zera już dziś, rozsądnym krokiem jest zapoznanie się z podstawami SQL, w tym z tabelami, kluczami głównymi, kluczami obcymi, łączeniami, indeksami oraz transakcjami, korzystając z narzędzia takiego jak PostgreSQL. Następnie poświęć czas na bezpośrednie łączenie się za pomocą sterownika, np. pakietu pg z Node.js, aby zrozumieć, w jaki sposób aplikacja komunikuje się z bazą danych bez żadnych pośredników. Dalej przejdź do Prismy i opanuj zasady tworzenia schematów, migracji, relacji oraz mechanizmy bezpieczeństwa typów. Na koniec spróbuj Drizzle, aby poznać, jak wygląda narzędzie do budowania zapytań oparte na SQL, wraz związane z nim kompromisy pod względem wydajności.

Gdzie to cię zaprowadzi

Celem ORM nigdy nie było zastąpienie SQL. Istnieje po to, by ulepszyć doświadczenie programisty, i nic nie jest ważniejsze od tego. Naprawdę dobry programista backendu nie jest definiowany przez biegłą znajomość Prisma czy Drizzle. To osoba, która rozumie, jak faktycznie działa baza danych, potrafi pisać SQL w razie potrzeby, wie, co ORM upraszcza za nią, a co równie ważne – wie, kiedy użycie ORM ma sens, a kiedy lepszym rozwiązaniem jest ręcznie napisany SQL. Gdy ta jasność się pojawi, wybór między tymi narzędziami przestaje być domysłem. Zaczynasz decydować na podstawie tego, czego projekt naprawdę potrzebuje, a nie na podstawie tego, co aktualnie jest popularne w danym miesiącu.

Literatura pokrewna

  • Zmniejszanie obciążenia Prismą i PostgreSQL przed płaceniem za większą bazę danych: Piętnaście praktycznych technik, od EXPLAIN ANALYZE i indeksów złożonych po rozwiązania typu N+1, liczniki, zarządzanie zasobami i procesy vacuum, służących do zmniejszenia liczby operacji w bazie danych Prisma.