Transaktionen mit Strapi v4
Wir haben Strapi bislang in zwei verschiedenartigen Projekten eingesetzt. Eines davon ist ein CMS-Projet, das andere ist ein Backend fĂŒr eine Next.JS-Anwendung.
In letzterem verwenden wir Transaktionen, um Ănderungen der Daten wĂ€hrend eines Importprozesses zu verarbeiten.
Wenn Sie mit dem Konzept der Transaktionen nicht vertraut sind, betrachten Sie es als ein Mittel zur Schaffung eines Referenzpunktes in der Zeit. Dieser Bezugspunkt ermöglicht es Ihnen, alle an der Datenbank vorgenommenen Ănderungen rĂŒckgĂ€ngig zu machen, wenn wĂ€hrend des Prozesses etwas schief lĂ€uft. Hier ist eine Definition von Transaktionen aus der PostgreSQL:
"Transaktionen sind ein grundlegendes Konzept aller Datenbanksysteme. Der wesentliche Punkt einer Transaktion ist, dass sie mehrere Schritte zu einer einzigen Alles-oder-Nichts-Operation bĂŒndelt. Die ZwischenzustĂ€nde zwischen den einzelnen Schritten sind fĂŒr andere gleichzeitige Transaktionen nicht sichtbar, und wenn ein Fehler auftritt, der den Abschluss der Transaktion verhindert, dann hat keiner der Schritte Auswirkungen auf die Datenbank."
Quelle: https://www.postgresql.org/docs/8.3/tutorial-transactions.html
Dieser Artikel zeigt Ihnen, wie wir Transaktionen in Strapi v4 verwenden und wie man Unit-Tests fĂŒr diese Transaktionen erstellt. Aber zunĂ€chst wollen wir erklĂ€ren, warum Transaktionen sehr sinnvoll sind.
Was ist der Grund fĂŒr die Verwendung von Transaktionen
In unserem Projekt, bei dem wir Strapi als Backend verwenden, mĂŒssen wir viele Daten auf einmal importieren (z. B. Tausende von Zeilen in einer csv-Datei). Diese Daten werden gelesen. Der angestossene Prozess erstellt oder aktualisiert dann die Zeilen in der Datenbank, die schliesslich als EntitĂ€ten im Strapi-Administrationsbereich erscheinen. Das Frontend fragt diese EntitĂ€ten dann ĂŒber das GraphQL-Plugin oder API-Endpunkte ab.
WĂ€hrend des Importprozesses ist es sehr wichtig, dass die Produktionsdatenbank nicht direkt ĂŒberschrieben wird. Andernfalls könnten wĂ€hrend eines Imports auftretende Probleme zu einer Datenbank fĂŒhren, in der einige Daten geĂ€ndert, wĂ€hrend andere Daten möglicherweise nicht eingefĂŒgt oder aktualisiert wurden. Dies hat zur Folge, dass die Daten beschĂ€digt sind und die Benutzer falsche (nicht vollstĂ€ndig aktualisierte) Daten sehen.
Es gibt einige Lösungen fĂŒr dieses Problem, aber bis heute ist keine offiziell dokumentiert. Wir haben uns fĂŒr Transaktionen entschieden.
Schauen wir uns nun an, wie wir Transaktionen mit Strapi v4 verwenden.
Wie wir Transaktionen verwenden
Im Hintergrund verwendet Strapi Knex.js, einen SQL Query Builder fĂŒr viele Arten von Datenbanken (in diesem Beispiel verwenden wir PostgreSQL), der Transaktionen unterstĂŒtzt.
Lassen Sie uns fĂŒr dieses Beispiel einen sehr einfachen Inhaltstyp erstellen. Er wird Produkt genannt und hat zwei Eigenschaften (Name und Preis):
Nun zu den Transaktionen!
Wir können das Knex-Objekt direkt von der globalen Strapi-Instanz abrufen:
/* global strapi */ const knexObject = strapi.db.connection
Und wir können mit dieser Methode eine Transaktion erstellen:
/* global strapi */ const newTransaction = strapi.db.connection.transaction()
Mit dieser Konstante newTransaction können Sie beliebige Ănderungen an der Datenbank vornehmen und jederzeit ein Commit oder Rollback durchfĂŒhren.
Hier sehen Sie, wie Sie ein neues Produkt mit dieser Methode einfĂŒgen:
/* global strapi */ const newTransaction = strapi.db.connection.transaction() newTransaction('products').insert({ name: 'My new product', price: 3.50, }, '*')
Das neue Produkt ist jetzt Teil der newTransaction, es ist noch nicht in der Datenbank vorhanden. Wir haben nun die Wahl zwischen Commit (Ăbernahme der Ănderungen) oder Rollback (ZurĂŒckgehen auf den Zeitpunkt/Zustand vor dem Start der Transaktion).
So wird die Transaktion ĂŒbertragen:
newTransaction.commit()
So machen Sie die Transaktion rĂŒckgĂ€ngig:
newTransaction.rollback()
Testen
Testen wir nun die beiden FÀlle und finden wir die EntitÀt mit Strapi:
Commit
/* global strapi */ const data = { name: 'My new Product', price: 3.5, } it('Inserts a product with a transaction', async () => { const newTransaction = await strapi.db.connection.transaction() const insertedRows = await newTransaction('products').insert(data, '*') expect(insertedRows).toHaveLength(1) // Find it with Strapi - still not present in the database const productResult = await strapi.entityService.findMany( 'api::product.product', { filters: data, } ) expect(productResult).toHaveLength(0) await newTransaction.commit() // Find it with Strapi - found it! const productResult = await strapi.entityService.findMany( 'api::product.product', { filters: data, } ) expect(productResult).toHaveLength(1) const newProduct = productResult[0] expect(newProduct).toMatchObject(data) await strapi.entityService.delete('api::product.product', newProduct.id) })
In diesem Test können Sie sehen, dass wir zuerst eine Transaktion erstellen und dann eine neue Zeile in die Transaktion einfĂŒgen. Wenn wir vor dem Commit mit Strapi nach der EntitĂ€t suchen, finden wir sie nicht. Wenn wir dann die Transaktion festschreiben, können wir mit dem EntitĂ€tsdienst von Strapi nach der neuen Zeile suchen, und tatsĂ€chlich finden wir sie.
Rollback
/* global strapi */ const data = { name: 'My new Product', price: 3.5, } it('Rollbacks a transaction', async () => { const newTransaction = await strapi.db.connection.transaction() const insertedRows = await newTransaction('products').insert(data, '*') expect(insertedRows).toHaveLength(1) await newTransaction.rollback() // Make sure the data is not inserted with Strapi const productResult = await strapi.entityService.findMany( 'api::product.product', { filters: data, } ) expect(productResult).toHaveLength(0) })
Hier sehen wir, dass die insertedRows vor dem Rollback der Transaktion eine LĂ€nge von eins haben. Sobald wir beschliessen, die Ănderungen nicht anzuwenden, findet Strapi die neue Zeile nicht mehr â genau das erwarten wir.
Fazit
Wir haben gesehen, wie man Transaktionen in Strapi v4 verwendet, um entweder neue Daten zu ĂŒbertragen oder die gesamte Transaktion zurĂŒckzunehmen. Beide Tests zeigen uns, wie sie verwendet werden und, dass Transaktionen mit Knex funktionieren.
NatĂŒrlich kann man mehr tun als nur EinfĂŒgen. Knex hat eine gute Dokumentation darĂŒber, wie man Daten aktualisiert, löscht, mit Beziehungen arbeitet, usw.
Dies ist eine grossartige Lösung, um unsere Daten unversehrt zu halten und sicherzustellen, dass wir die Benutzer nicht stören, wĂ€hrend wir im Hintergrund Daten manipulieren. In einem spĂ€teren Artikel werden wir erlĂ€utern, wie wir die UnterstĂŒtzung von Typescript zu Transaktionen hinzugefĂŒgt haben. Das Konzept bleibt das gleiche, und es wird Ihnen noch mehr Kontrolle ĂŒber die Daten ermöglichen.
Autor: Geoffroy Baumier, Senior Software Engineer & Team Lead
Ihr Kontakt bei UFirst
JorÂdĂĄn JaÂrolĂm
Beginnen Sie Ihre digitale Zukunft mit uns.
Wir freuen uns auf Sie!