Raw SQL, Prisma oder Drizzle: Die Wahl der Datenbankschicht für Typsicherheit und Kontrolle
Wie sich roher SQL, Prisma und Drizzle in Bezug auf Kontrolle, Typsicherheit und Entwicklererfahrung bei PostgreSQL unterscheiden sowie eine praktische Methode, um für eine TypeScript-Anwendung das Richtige auszuwählen.
Quellen: Prisma vs Drizzle vs Raw SQL: What Every Backend Developer Should Understand Before Choosing an ORM
Die meisten Arbeiten im Backend beginnen damit, eine API zum Antworten zu erhalten. Man konfiguriert Routen, fügt einen oder zwei Controller hinzu, integriert Authentifizierung sowie einige Middleware – und für eine Weile ist das das ganze Bild. Dann taucht eine schwierigere Frage auf: Wie sollte die Anwendung eigentlich mit ihrer Datenbank kommunizieren? Plötzlich gibt es eine Menge Begriffe (roher SQL, Treiber, ORMs, Abfragebauer, Prisma, Drizzle) sowie starke Meinungen in alle Richtungen. Einige Entwickler berühren keinen ORM. Andere behaupten, Prisma habe ihnen Wochen Zeit gespart. Eine dritte Gruppe wählt Drizzle, weil es SQL sichtbar lässt anstatt es zu verbergen. Die folgenden Abschnitte erläutern das Thema von Grund auf, sodass die Entscheidung zwischen rohem SQL, Prisma und Drizzle für einen TypeScript- und PostgreSQL-Stack letztendlich eine technische Abwägung hinsichtlich Kontrolle und Typsicherheit ist – und nicht eine Frage des Trends.
Wie eine Anfrage die Datenbank erreicht
Bevor ein Toolname irgendetwas bedeutet, hilft es, nachzuvollziehen, wie eine Anfrage durch einen typischen Backend-System verläuft. Jemand besucht Ihre Website und klickt auf eine Schaltfläche wie „Mein Profil anzeigen“. Diese Aktion beginnt im Browser, gelangt über Ihre API in die Datenbankschicht und schließlich zur Datenbank selbst, bevor eine Antwort den Rückweg antreten kann.
Browser
|
vExpress API |
vDatabase Layer |
vPostgreSQL Database |
vResponse back to the user
Die Datenbankschicht in der Mitte ist der Ort, an dem sich die Verwirrung konzentriert. Ihre Anwendung benötigt einen Mechanismus, um mit der Datenbank zu kommunizieren, und es gibt drei gängige Methoden: das Manuell-Einschreiben von rohem SQL, die Verwendung eines ORM wie Prisma oder die Nutzung eines Abfrageschreibers wie Drizzle. Jeder der folgenden Abschnitte befasst sich nacheinander mit einer dieser Methoden.
Manuelles Schreiben von SQL
Der direkteste Weg ist zugleich der älteste: Sie schreiben den SQL selbst. Stellen Sie sich eine einfache Benutzer-Tabelle mit den üblichen Spalten vor.
users
id
name
email
password
Das Abrufen eines einzelnen Benutzers ist dann äußerst einfach.
SELECT *
FROM users
WHERE id = 1;
Weil die Datenbank SQL als ihre native Sprache verwendet, wird nichts beim Ein- oder Auslesen übersetzt. In Node.js mit PostgreSQL ist das pg-Paket die übliche Methode, um Abfragen direkt zu senden. Daraus importiert man einen Connection Pool.
import { Pool } from "pg";
Dann konfigurieren Sie den Pool über eine Umgebungsvariable und führen eine parametrisierte Abfrage dagegen aus, um die resultierenden Zeilen zu lesen.
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);
Ihr Code sendet SQL, PostgreSQL führt ihn aus, und die Zeilen kommen direkt zurück, ohne dass etwas dazwischen abstrahiert wird. Wenn Sie den SQL-Code selbst schreiben, haben Sie die volle Kontrolle. Komplexe Berichte können beispielsweise direkt formuliert werden, wie zum Beispiel das Verknüpfen von Benutzern mit ihren Bestellungen sowie das Gruppieren, um die Anzahl der Bestellungen pro Benutzer zu zählen.
SELECT
users.name,
COUNT(orders.id)
FROM users
JOIN orders
ON users.id = orders.user_id
GROUP BY users.name;
Für solch komplexe analytische Abfragen ist handgeschriebener SQL tatsächlich kaum zu übertreffen. Seine Erstellung zwingt einen außerdem dazu, sich mit Verknüpfungen, Indizes, Ausführungsplänen sowie der allgemeinen Leistung vertraut zu machen – und dieses Wissen bleibt unabhängig davon wertvoll, welche Tools man später verwendet. Zudem gibt es keine zusätzliche Schicht zwischen dem Code und dem Engine.
Die Nachteile treten auf, sobald ein Projekt mehr als nur wenige Abfragen umfasst. Stellen Sie sich eine Codebasis vor, in der Hunderte solcher Abfragen verteilt sind, darunter viele, die wie diese Suche nach E-Mail-Adressen aussehen.
const result = await pool.query(
`
SELECT *
FROM users
WHERE email=$1
`,
[email]
);
Nehmen wir nun an, jemand ändert den Namen einer Spalte und verwandelt email in email_address. Jede Abfrage, die diese Spalte verwendet, funktioniert dann stillschweigend nicht mehr. Die Datenbank registriert die Änderung sofort, doch Ihr TypeScript-Code merkt nichts davon, bis etwas zur Laufzeit fehlschlägt. Es gibt eine subtilere Variante derselben Gefahr. Angenommen, Sie holen sich die erste Zeile.
const user = result.rows[0];
Dann liest man eine Eigenschaft daraus – mit einem Tippfehler.
console.log(user.emial);
Der falsch eingegebene emial löst bei JavaScript keine Fehlermeldung aus. Man bemerkt ihn erst, wenn diese Zeile ausgeführt wird und vor dem Benutzer zu Fehlern führt. Genau diese Art von Problemen ist der Grund dafür, dass ORMs überhaupt existieren.
Was ein ORM wirklich ist
ORM steht für Object Relational Mapping, was klingt, als wäre es etwas Großartiges – in Wirklichkeit bedeutet es lediglich, dass es eine Brücke zwischen der Programmiersprache und der Datenbank gibt. Anstatt eine solche Abfrage manuell zu schreiben,
SELECT *
FROM users
WHERE id=1;
Man schreibt etwas, das einer Methodenaufruf auf einem Objekt ähnelt.
user.findUnique({
id: 1
})
Der ORM nimmt das und erzeugt im Hintergrund die eigentlichen SQL-Anfragen, sodass Ihr Code über den ORM zu einer generierten Abfrage und anschließend zur Datenbank gelangt.
Your TypeScript code
|
vORM |
vSQL query |
vDatabase
Teams greifen auf ORMs zurück, weil moderne Anwendungen mehr als nur ein Mittel zum Senden von Abfragen benötigen. Sie verlangen Typsicherheit, echte Migrationsfunktionen, eine Schema-Verwaltung, die nicht davon abhängt, sich jedes Tabellenmuster auswendig zu merken, sowie deutlich weniger wiederholenden Boilerplate-Code. In der heutigen TypeScript-Welt sind die am häufigsten genannten Tools Prisma, Drizzle, TypeORM und Sequelize. Der Fokus liegt hier auf reinem SQL, Prisma und Drizzle, da diese drei Tools das abdecken, was die meisten modernen TypeScript-Backends tatsächlich in Betracht ziehen.
Prisma kennenlernen
Prisma gehört zu den am meisten diskutierten ORMs im TypeScript-Ökosystem und wird oft als erstes in Gesprächen über moderne Node.js-Backends erwähnt. Im Kern ermöglicht es einer TypeScript-Anwendung, über gewöhnlichen TypeScript mit einer Datenbank zu kommunizieren, anstatt über überall verstreuten SQL-Code – außerdem unterstützt es PostgreSQL, MySQL, SQLite, SQL Server und MongoDB. Man schreibt TypeScript, und Prisma wandelt dies in Abfragen um.
TypeScript code
|
vPrisma Client |
vSQL queries |
vDatabase
Eine typische Express-, Prisma- und PostgreSQL-Einrichtung ist strukturiert organisiert, wobei Anfragen über die API eintreffen, eine Service-Schicht durchlaufen und schließlich über den Prisma Client auf die Datenbank zugreifen.
Client
|
vExpress API |
vService Layer |
vPrisma Client |
vPostgreSQL Database
Prisma Client ist der Teil, der die eigentliche Arbeit erledigt: die eigentliche Brücke zwischen Ihrem Backend-Code und der dahinterliegenden Datenbank. Alles beginnt mit einer einzigen Datei, schema.prisma, die die gesamte Datenbankstruktur an einem Ort beschreibt. Ein Benutzermodell deklariert beispielsweise vier typisierte Spalten.
model User {
id Int @id @default(autoincrement())
name String
email String @unique
password String
}
Dieser Block weist Prisma an, eine User-Tabelle mit diesen Spalten zu erstellen, und Prisma versteht genau, was damit gemeint ist. Während bei rohem SQL eine explizite Anweisung erforderlich wäre, um dieselbe Tabelle zu erstellen,
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT,
email TEXT UNIQUE,
password TEXT
);
Prisma benötigt nur den obigen Schema-Block und kümmert sich darum, ihn durch eine Migration in tatsächliche Datenbankänderungen umzuwandeln. Eine Migration ist im Grunde eine aufgezeichnete Historie davon, wie sich die Datenbank im Laufe der Zeit verändert hat. Angenommen, Ihre Benutzer-Tabelle beginnt sehr einfach.
User
id
name
email
Monate später möchten Sie eine Telefonnummer hinzufügen. Anstatt die Tabelle manuell zu ändern, führen Sie einen einzigen Befehl aus.
npx prisma migrate dev --name add_phone_number
Prisma erzeugt eine Migrationsdatei, um diese Änderung festzuhalten, sodass die Datei von einer Migrationsdatei für die anfängliche Einrichtung zu einer weiteren wird, die die Telefonnummer hinzufügt. Die gesamte Historie bleibt dabei versioniert und existiert nicht nur im Gedächtnis einer Person. Sobald das Schema definiert ist, erstellt Prisma einen Client, den man im Code verwendet, um beispielsweise einen Benutzer anhand seiner ID abzurufen.
const user = await prisma.user.findUnique({
where:{
id:1
}
});
Hinter den Kulissen wird es leise zu gewöhnlichem SQL.
SELECT *
FROM users
WHERE id=1;
Man musste den SQL-Code niemals selbst eingeben. Auch die täglichen Operationen lassen sich intuitiv zuordnen: Ein Einfügen wird zu einem create-Aufruf, das Abrufen aller Daten zu einem find-many-Aufruf, das Aktualisieren eines Datensatzes zu einem update-Aufruf unter Verwendung einer where-Klausel und das Löschen zu einem delete-Aufruf. Wo Prisma seinen Ruf am meisten verdient, ist bei der Typsicherheit. Wenn Ihre Schema einen User mit id, Name und E-Mail-Adresse definiert, bietet die Abfragemöglichkeit eine echte Autocomplete-Funktion für jedes Feld.
const user = await prisma.user.findMany();
user[0].email;
Falls man das versehentlich als user[0].emali eintippt, markiert Prisma zusammen mit TypeScript dies bereits vor dem Start der Anwendung, was im Laufe der Zeit erheblich Zeit beim Debuggen spart. Reale Anwendungen haben natürlich selten nur eine Tabelle. Angenommen, ein Benutzer besitzt viele Beiträge – dann gibt es eine Seite für Benutzer und eine Seite für Beiträge, die über eine user id miteinander verknüpft sind.
User
id
namePostid
title
userId
Prisma drückt diese Beziehung direkt im Schema aus, sowohl im Benutzermodell als auch anderswo.
model User {
id Int @id @default(autoincrement())
und das Post-Modell, das über eine Beziehung auf seinen Benutzer verweist.
name String posts Post[]
}model Post { id Int @id @default(autoincrement()) title String userId Int user User @relation(
fields:[userId],
references:[id]
)}
Das Abrufen eines Benutzers zusammen mit seinen Beiträgen wird dann zu einem lesbaren Aufruf, der die damit verbundenen Datensätze enthält.
const user = await prisma.user.findUnique({
where:{
id:1
},
include:{
posts:true
}
});
Das gibt ein Benutzerobjekt mit einem verschachtelten Array von Beiträgen zurück – genau die Struktur, die man in Anwendungscode benötigt. Die Stärken von Prisma liegen darin, wie natürlich die Entwicklererfahrung ist, wie stark die Typsicherheit in TypeScript-Projekten ist, wie geordnet die Migrationsprozesse ablaufen, wie umfassend die Dokumentation ist und wie schnell ein ganzes Team das Schema lesen sowie die Datenbankstruktur verstehen kann.
Es sind Kompromisse unvermeidlich. Echt komplexe Berichterstattung kann im Vergleich zu handgeschriebenen SQL-Abfragen unhandlich erscheinen. Da Prisma SQL so effektiv versteckt, haben Entwickler, die nur Prisma erlernen, manchmal Schwierigkeiten, wenn sie später auf der Ebene der Datenbank optimieren müssen. Zudem gibt es technisch gesehen einen zusätzlichen Schritt im Anfragenweg – von der Anwendung über Prisma zu SQL und schließlich zur Datenbank – anstelle des kürzeren Weges von Anwendung direkt zu SQL und dann zur Datenbank. Für die meisten Anwendungen stellt diese zusätzliche Schicht kein Problem dar, doch es ist sinnvoll, zu wissen, dass sie existiert. Prisma eignet sich am besten, wenn man TypeScript-APIs entwickelt, eine schnelle Entwicklung wünscht, hauptsächlich standardmäßige Funktionen wie Erstellen, Lesen, Aktualisieren und Löschen benötigt und großen Wert auf die Entwicklererfahrung legt – genau deshalb setzen viele Startups und SaaS-Produkte darauf.
Wo Drizzle passt
Drizzle ist ein weiterer ORM, der in TypeScript an Bedeutung gewonnen hat. Um ihn zu verstehen, muss man zunächst erkennen, dass Prisma und Drizzle auf völlig unterschiedlichen Philosophien beruhen. Prisma versucht, Entwickler so weit wie möglich von der Komplexität der Datenbanken fernzuhalten. Drizzle verfolgt hingegen die entgegengesetzte Strategie: Es hält Entwickler in der Nähe von SQL und fügt einfach die Sicherheitsfunktionen von TypeScript darauf hinzu. Beide zielen auf dasselbe Ziel ab – eine bessere Arbeitsweise mit der Datenbank –, doch durch völlig unterschiedliche Wege. Drizzle ist ein leichtgewichtiger TypeScript-ORM für PostgreSQL, MySQL und SQLite, der sich auf Typsicherheit, Leistung, Nähe zu SQL sowie eine schlanke Architektur konzentriert. Man schreibt weiterhin TypeScript, das über Drizzle in eine Abfrage umgewandelt und anschließend an die Datenbank gesendet wird.
TypeScript code
|
vDrizzle ORM |
vSQL query |
vDatabase
Wenn man die beiden nebeneinander stellt, wird die philosophische Kluft deutlich. Das Abrufen aller Benutzer in Prisma erfolgt auf die folgende Weise, und der darunter generierte SQL-Code kommt selten in den Sinn.
const user = await prisma.user.findMany();
Drizzle verlangt etwas, das viel näher am eigentlichen SQL ist.
const users = await db
.select()
.from(usersTable);
Man kann das Verhalten auf Datenbankebene praktisch direkt aus dem Code ablesen. Auch die Schemata unterscheiden sich. Prisma speichert sie in einer speziellen schema.prisma-Datei, während Drizzle das Schema in reinem TypeScript deklariert, wobei zuerst die benötigten Spaltenkonstrukte importiert werden.
import {
pgTable,
serial,
varchar
} from "drizzle-orm/pg-core";
und die Tabelle mit vorgegebenen Spalten deklarieren.
export const users = pgTable("users", { id: serial("id").primaryKey(), name: varchar("name"), email: varchar("email")});
Die Struktur Ihrer Datenbank befindet sich somit direkt innerhalb der TypeScript-Codebasis, und es gibt kein zusätzliches Schema-Dialekt, das berücksichtigt werden müsste. Tägliche Operationen ähneln SQL, das in TypeScript-Kleidung gehüllt ist: Ein Einfügen ist ein Aufruf mit Werten, das Lesen aller Daten erfolgt durch einen SELECT-Befehl von der Tabelle, und das Filtern fügt eine WHERE-Klausel hinzu, die aus einem Gleichheits-Hilfsfunktion erstellt wird. Menschen ziehen Drizzle aus mehreren Gründen vor. Es ist wirklich leichtgewichtig und weist nur geringe Abstraktionen auf. Da es SQL nahe bleibt, schärfen sich Ihre SQL-Fähigkeiten weiter, anstatt hinter Abstraktionen zu verblassen. Die generierten Abfragen sind in der Regel vorhersehbar, da weniger Zwischenschritte zwischen Ihrer Absicht und dem Ergebnis bestehen. Zudem wurde es mit modernen Laufzeiten im Sinn entwickelt, einschließlich Edge- und Serverless-Umgebungen.
Andererseits kann Drizzle für diejenigen, die noch nie wirklich SQL gelernt haben, anfangs verwirrender wirken als Prisma, das im Allgemeinen eine sanftere Einführung für Anfänger bietet. Um Drizzle gut zu nutzen, ist es tatsächlich notwendig, Joins, Beziehungen, Indizes sowie das Verhalten von Abfragen zu verstehen – ohne etwas davon zu umgehen. Zwar sind sein Ökosystem, die Dokumentation und die Community solide, doch sie bleiben kleiner als Prismas ausgereiftere Umgebung. Die klarste Art, den Unterschied darzustellen, ist folgende: Bei Prisma betrachtet man die Datenbank als eine Sammlung von Objekten, wobei der Schwerpunkt auf der Entwicklererfahrung liegt, während man bei Drizzle die Datenbank eher wie SQL selbst behandelt und der Fokus auf Leistung und Kontrolle verlagert wird.
Die drei Ansätze nebeneinander
Wenn man alle drei betrachtet, ergeben sich aus ihren Architekturen ein Spektrum davon, wie viele Schichten zwischen Anwendung und Datenbank liegen.
Raw SQL: Application → SQL Query → Database
Prisma: Application → Prisma Client → SQL → Database
Drizzle: Application → Drizzle → SQL → Database
Betrachten Sie eine einfache Operation, bei der ein Benutzer über die E-Mail-Adresse in einer grundlegenden Benutzer-Tabelle gefunden wird. Im reinen SQL werden direkt parametrisierte Abfragen gesendet.
const result = await pool.query(
`
SELECT *
FROM users
WHERE email=$1
`,
["rahul@test.com"]
);
In Prisma rufen Sie eine eindeutige Abfrage auf.
const user =
await prisma.user.findUnique({
Das E-Mail-Adress in einer WHERE-Klausel übergeben.
where:{
<email:"rahul@test.com>"
}});
In Drizzle erstellt man eine Auswahl, die durch eine Gleichheit in der E-Mail-Spalte eingeschränkt wird.
const user =
await db
.select()
.from(users)
.where(
eq(
users.email,
"rahul@test.com"
)
);
Am Ende liefern alle drei dieselben Daten. Was unterschiedlich ist, ist das Ausmaß an Kontrolle, das man haben möchte, wie viel Abstraktion man verträgt, und wie viel SQL man bereits kennt. Ein häufiger Anfängerfehler besteht darin, sich nur auf das schnellste Tool zu konzentrieren, obwohl in echten Projekten Geschwindigkeit selten die entscheidende Frage ist. Bessere Fragen lauten, womit das Team bereits vertraut ist, wie komplex das Projekt tatsächlich ist, wie gut die Datenbankkenntnisse aller Beteiligten sind und wie kompliziert die Abfragen voraussichtlich werden werden. Eine einfache SaaS-App reicht in der Regel bereits mit Prisma aus, während bei datenbankintensiven Anwendungen Drizzle oder an manchen Stellen rohes SQL an Bedeutung gewinnt.
Zusammenfassend ausgedrückt: Die Lernkurve ist bei rohem SQL am schwierigsten, bei Prisma am einfachsten und bei Drizzle mittel. Die Kontrolle über SQL ist bei rohem SQL am größten, bei Drizzle hoch und bei Prisma am geringsten. Die Typsicherheit ist bei rohem SQL schwach, aber bei Prisma und Drizzle hervorragend. Die reine Leistung ist bei rohem SQL und Drizzle ausgezeichnet und bei Prisma gut. Die Entwicklererfahrung ist bei Prisma am besten, bei Drizzle gut und bei rohem SQL mittelmäßig. Komplexe Abfragen eignen sich besser für rohes SQL und Drizzle, wobei Prisma eine mittlere Leistung zeigt. Was die Benutzerfreundlichkeit für Anfänger angeht, führt Prisma, Drizzle liegt in der Mitte und rohes SQL ist am schwächsten.
Was sollten Sie also wirklich wählen?
Sollte ein ORM immer die Standardlösung sein? Ehrlich gesagt, nein. Wie jedes andere Werkzeug eignet sich ein ORM für bestimmte Aufgaben und nicht für andere – nichts passt zu jedem Projekt. Roher SQL hat seinen Platz, wenn maximale Kontrolle erforderlich ist, wenn die Anwendung stark auf Datenbanken angewiesen ist, wenn Abfragen komplex werden oder die Leistung unter keinen Umständen nachlassen darf. Analyseplattformen, Berichtssysteme und Finanzanwendungen sind geeignet, genauso wie alles, was große Datenmengen verarbeiten muss. Ein Bericht, der beispielsweise angibt, wie viel jeder Kunde über fünf Jahre gekauft hat sowie seinen durchschnittlichen Bestellwert, lässt sich natürlich leichter direkt in SQL formulieren, indem man die Aggregatfunktionen verwendet.
SELECT
customers.name,
COUNT(orders.id),
AVG(orders.amount)
und durch Verbinden sowie Gruppieren werden sie erzeugt.
FROM customersJOIN ordersON customers.id = orders.customer_idGROUP BY customers.name;
Für solche Aufgaben gibt es tatsächlich nichts, was das ausdrücken kann, was SQL leisten kann. Das Problem ist, dass die Verwendung von manuell geschriebenem SQL für alles in einer großen Codebasis schnell unübersichtlich wird – es entstehen Hunderte von Abfragedateien, die überall verstreut sind, und die Typsicherheit muss man selbst überwachen. Ein nach Prioritäten geordnetes Überblick über die Leistung der Node.js API zeigt, wie oft gerade die Datenbank und nicht das Framework der entscheidende Faktor für Erfolge oder Misserfolge ist.
Prisma ist in der Regel die bessere Wahl, wenn man einen TypeScript-Backend entwickelt, hauptsächlich standardmäßige Create-Read-Update-Delete-Aufgaben durchführt, die Entwicklungsgeschwindigkeit schätzt und mehrere Entwickler in einem Codebase koordiniert. Das beschreibt SaaS-Produkte, Admin-Dashboards, Storefront-APIs und Content-Management-Systeme, bei denen die täglichen Aufgaben darin bestehen, Benutzer anzulegen, Profile zu aktualisieren, Bestellungen aufzugeben oder Dashboard-Daten abzurufen. Prisma steigert dort tatsächlich die Produktivität, indem es eine rohe Abfrage in einen sauberen, lesbaren Aufruf verwandelt, den jeder Teamkollege auf einen Blick verstehen kann. Drizzle eignet sich, wenn man mit TypeScript arbeitet, bereits über ein solides Grundwissen in SQL verfügt, Wert auf Leistung und Kontrolle legt und etwas Leichtes statt Schwerem bevorzugt. Es spricht Entwickler an, die den Schutz eines ORMs wollen, ohne sich zu sehr vom Datenbank-System entfernt zu fühlen.
In der Praxis verfolgen viele reale Projekte keinen einzigen Ansatz. Ein Startup kann vernünftigerweise mit einem direkten Weg von Express über Prisma zu PostgreSQL beginnen.
Express → Prisma → PostgreSQL
Als erster Schritt ist das wirklich sinnvoll. Später, wenn sich das Produkt weiterentwickelt und größere Anforderungen an Analysen oder Berichte entstehen, fügen Teams oft rohe SQL-Abfragen in bestimmte Bereiche hinzu, anstatt Prisma vollständig zu ersetzen – wodurch die Anwendung über sowohl Prisma als auch handgeschriebene Abfragen auf PostgreSQL zugreift.
Application
├── Prisma
└── Raw SQL Queries
↓
PostgreSQL
Es ist keine Regel, überall denselben Ansatz zu verwenden, und die meisten tatsächlichen Codebasen mischen diese Ansätze letztendlich. Startups setzen oft auf TypeScript, Prisma und PostgreSQL allein aus Gründen der Geschwindigkeit. Neuere TypeScript-Projekte wenden zunehmend Drizzle sowie seinen leichtgewichtigen, SQL-freundlichen Stil an. Größere Systeme kombinieren häufig einen ORM mit rohem SQL sowie spezieller Datenbankoptimierung – einfach weil verschiedene Teile eines großen Systems unterschiedliche Probleme aufweisen.
Fehler, die man vermeiden sollte
Mehrere Fehlerquellen treten immer wieder auf. Die erste ist die Behandlung des ORMs, als wäre es die Datenbank selbst. Prisma ist keine Datenbank, genauso wenig wie Drizzle; sie sind Werkzeuge, um auf eine Datenbank zuzugreifen – egal ob PostgreSQL, MySQL oder MongoDB dahinterliegt. Die zweite Fehlerquelle ist das vollständige Umgehen von SQL und der direkte Einsatz von Prisma. Zunächst scheint das effizient zu sein, führt aber in der Regel dazu, dass Probleme auftauchen, sobald langsame Abfragen nicht mehr nachvollziehbar sind, Join-Operationen unnötig kompliziert erscheinen und eine Optimierung aufgrund fehlender Grundlagen unter der Abstraktion äußerst schwierig wird. Ein ORM soll niemals SQL ersetzen; es befindet sich darüber. Die dritte Fehlerquelle ist das Streben nach reinen Leistungszahlen um jeden Preis. Die Produktivität der Entwickler ist in der Praxis genauso wichtig, und fünf Millisekunden Ersparnis bei einer Abfrage auf Kosten von drei Monaten langsamerer Entwicklung sind selten der klügere Handel.
Ein Lernpfad, der sich auszahlt
Wenn man heute von Null anfängt, ist eine sinnvolle Reihenfolge, mit den Grundlagen von SQL zu beginnen – einschließlich Tabellen, Primärschlüsseln, Fremdschlüsseln, Vernetzungen, Indizes und Transaktionen – unter Verwendung von Tools wie PostgreSQL. Anschließend sollte man direkt über einen Driver wie das pg-Paket von Node mit der Datenbank kommunizieren, um zu verstehen, wie eine Anwendung ohne Zwischenschritte mit einer Datenbank spricht. Danach kann man zu Prisma übergehen und sich mit Schemata, Migrationsprozessen, Beziehungen sowie der Typsicherheit vertraut machen. Schließlich sollte man Drizzle ausprobieren, um zu erfahren, wie ein auf SQL basierender Abfrageschreiber funktioniert und welche Leistungsabstriche er mit sich bringt.
Wo man dadurch ankommt
Zweck eines ORM war niemals, SQL zu ersetzen. Es dient dazu, die Arbeit des Entwicklers zu verbessern – nichts Größeres gibt es. Ein wirklich guter Backend-Entwickler wird nicht dadurch definiert, dass er Prisma oder Drizzle gut kennt. Es handelt sich dabei um jemanden, der versteht, wie eine Datenbank tatsächlich funktioniert, SQL schreiben kann, wenn es erforderlich ist, weiß, was ein ORM für ihn vereinfacht, und – genauso wichtig – weiß, wann es sinnvoll ist, auf ein ORM zurückzugreifen, und wann handgeschriebener SQL die bessere Wahl ist. Sobald diese Klarheit vorhanden ist, ist die Auswahl zwischen diesen Werkzeugen keine Vermutung mehr. Man entscheidet dann danach, was das Projekt wirklich benötigt, anstatt danach, was gerade im Moment in Mode ist.
Verwandte Artikel
- Gemeine Fehlvorstellungen zu Node.js und Datenbanken, die Produktionsfehler verursachen: Erfahren Sie, warum async/await, Connection Pooling und ORMs nicht automatisch Rennbedingungen, Verbindungserschöpfung oder SQL-Injection in Node.js-Anwendungen verhindern.
- Ausgleich zwischen Prisma CRUD-Generierung und zielgerichteter Route-Kontrolle: Ermitteln Sie, wie die schema-getriebene Erstellung von Prisma CRUD-Routern wiederholenden Boilerplate-Code beseitigen kann, während Entscheidungen bezüglich Vertrauens, Scope und Zugriffsmöglichkeiten im Anwendungscode bleiben.