Newsletter

Bleiben Sie auf dem Laufenden!

Abonnieren Sie unseren Newsletter – Sie erhalten so News, Tipps und Tricks rund um das Thema E-Commerce.

Technologie

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.

Grund fĂŒr Transaktionen

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.

Verwendung

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

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

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

Portrait Jor­dån Ja­rolím
Partner & Tech. Architekt

Jor­dån Ja­rolím

Beginnen Sie Ihre digitale Zukunft mit uns.
Wir freuen uns auf Sie!