Collections & Datensätze

Die vierundzwanzig Datensatztypen neben den Lieferungen — der Snapshot-Umschlag, den sie teilen, was ein Snapshot ist und wie Sie einen durchlaufen.

Lieferungen sind eine kuratierte Ressource, von Hand geformt, weil der Wareneingang das Erste war, wonach überhaupt jemand gefragt hat. Collections sind der Rest der Aufzeichnungen: Temperaturen, Kühlzyklen, Reinigung, Fritteusenkontrollen, Etiketten, vierundzwanzig an der Zahl, ausgeliefert über eine einzige generische Struktur.

Wo der Liefer-Endpunkt Ihnen ein entworfenes Objekt gibt, gibt Ihnen eine Collection den Datensatz so, wie die App ihn hält, verpackt in einen Umschlag, der sagt, wann er erfasst wurde und ob es ihn noch gibt. Dieser Tausch ist Absicht: Er ist der Grund, warum ein neues Modul diese API in der Woche erreicht, in der es erscheint, und nicht erst im Quartal danach.

Jede Collection ist eine eigene Zuweisung. Ein Schlüssel liest temperature-records, weil jemand temperature-records:read zugewiesen hat, und nichts sonst kommt damit.

Was Sie lesen können

GET /v1/restaurants/{restaurantId}/collections
GET /v1/restaurants/{restaurantId}/collections/{collection}/records?limit=…&cursor=…
GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}
GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}/assets

Beginnen Sie mit dem ersten. Er listet nur die Collections, die Ihrem Schlüssel zugewiesen wurden, jede mit der Berechtigung, die sie geöffnet hat, Sie müssen also nie raten:

{
  "data": [
    {
      "collection": "temperature-records",
      "readScope": "temperature-records:read",
      "description": "Received shadow snapshots for temperature-records; not complete primary-store history.",
      "stateKind": "received-shadow-snapshot",
      "payloadVersion": 1
    }
  ]
}

Ein leeres data ist kein Fehler und keine Störung: Es ist ein Schlüssel ohne Collection-Zuweisungen. Die meisten Schlüssel, die ausgestellt wurden, bevor es diese Oberfläche gab, haben genau das, und einen zu erweitern ist eine E-Mail.

Der Umschlag

Jeder Datensatz in jeder Collection trägt dieselben äußeren Felder. Nur data ändert seine Struktur.

FeldTypBedeutung
idstringDie Kennung des Datensatzes, stabil und eindeutig innerhalb der Collection.
restaurantIdstringDas Restaurant, zu dem er gehört. Spiegelt den Pfad.
collectionstringAus welcher Collection er stammt. Spiegelt den Pfad.
dataobject | fehltDer Datensatz selbst. Eine Löschmarkierung ist der einzige Fall, in dem es fehlen kann; überall sonst dürfen Sie es als vorhanden behandeln.
deletedbooleantrue bedeutet eine Löschmarkierung: Der Datensatz wurde in der App gelöscht.
capturedAtstringWann das Gerät die Änderung erfasst hat, als Zeichenkette mit Millisekunden seit der Epoche.
receivedAtstringWann diese API sie empfangen hat. Später als capturedAt, manchmal um Stunden.
sequencestringEine monoton steigende Position im Strom der Änderungen. Vergleichen Sie zwei davon; rechnen Sie nicht mit einer.
mutationIdstringDie Kennung der Änderung, die diesen Zustand erzeugt hat. Auf unserer Seite ein Idempotenzschlüssel, auf Ihrer ein Schlüssel zur Deduplizierung.
sourceVersionstring | nullDer Versionsstempel des ursprünglichen Datensatzes, sofern er einen hat.
payloadVersionnumberDerzeit immer 1. Er steigt, falls sich die Bedeutung von data je ändert.
stateKindstringImmer received-shadow-snapshot. Lesen Sie den nächsten Abschnitt.

capturedAt und receivedAt zählen beide. Ein Tablet in einem Kühlraum ohne Empfang erfasst um 09:00 Uhr und lädt um 14:00 Uhr hoch; Ihre eigene Pipeline nach receivedAt zu ordnen hält sie korrekt, und nach capturedAt zu berichten hält sie wahr.

Was ein Snapshot ist und was nicht

stateKind sagt received-shadow-snapshot, und die Formulierung ist mit Bedacht gewählt.

  • Es ist der aktuelle Zustand eines Datensatzes, kein Protokoll jeder Änderung. Lesen Sie einen Datensatz zweimal, und Sie bekommen beide Male, wie er jetzt aussieht.
  • Es ist, was wir empfangen haben, nicht, was das Restaurant hält. Ein Gerät, das eine Änderung nie hochgeladen hat, heißt: ein Datensatz, den diese API nie gesehen hat.
  • Es ist keine rückwirkende Nachlieferung. Eine Collection beginnt für ein Restaurant an dem Tag, an dem die Erfassung dafür eingeschaltet wird. Datensätze, die davor entstanden sind, liegen in der App und nicht hier.

Ein fehlender Datensatz ist also wirklich mehrdeutig: nie erfasst, oder erfasst und noch nicht angekommen. Bauen Sie darauf keine Zahl, die sich wie ein Audit liest, und lesen Sie die Anmerkung zur Vollständigkeit, bevor Sie irgendjemandem eine Anzahl nennen.

Löschmarkierungen sind der eine Fall, in dem Abwesenheit eindeutig ist. deleted: true ohne data heißt, dass der Datensatz existiert hat und gelöscht wurde, und es ist der einzige Weg, auf dem Sie erfahren, dass etwas verschwunden ist.

Die vierundzwanzig Collections

Jede davon nimmt <collection>:read als Berechtigung — cooling braucht cooling:read, und so weiter die Liste hinunter.

CollectionWas ein Datensatz ist
restaurantsDer Standort selbst: Name, Adresse, Schließtage, Abonnement und Einstellungen.
usersDie Mitarbeiterkonten, die Kontrollen erfassen, und welche Module sie nutzen.
areasDie Zonen, in die ein Restaurant aufgeteilt ist — Küche, Kühlraum, Bar.
equipmentKühlschränke, Gefriergeräte und Kühlketten-Einheiten, mit ihren Min-/Max-Schwellen.
sensorsDie Funkfühler, über ihre MAC-Adresse, und das Gerät, das jeder von ihnen überwacht.
temperature-recordsEine Temperatur, die eine Person an einem Gerät gemessen hat, mit der Schicht und einer etwaigen Korrekturmaßnahme.
temperature-readingsEine Temperatur, die ein Sensor von sich aus gemeldet hat, unbeaufsichtigt.
suppliersWer liefert, mit Kontaktwegen und Kundennummer.
productsDie Produkte, die angenommen und verwendet werden.
preparationsEigene Zubereitungen, mit Haltbarkeit und Allergenen.
cleaning-tasksDer Reinigungsplan: jede Aufgabe, ihr Bereich, ihre Wiederholung und ob sie ein Foto verlangt.
cleaning-task-recordsEine tatsächlich erledigte Reinigungsaufgabe — wann und von wem.
cleaning-task-picturesDas Foto, das es belegt. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten.
coolingEin Kühlzyklus: Produkt, Anfangs- und Endtemperatur, Anfangs- und Endzeit.
freezingEin Gefriervorgang, dieselbe Struktur wie beim Kühlen.
reheatingEin Wiedererhitzungsvorgang, dieselbe Struktur wie beim Kühlen.
transportEin transportiertes Produkt, mit Abgangs- und Ankunftsort, Zeit und Temperatur.
fryer-equipmentDie Fritteusen.
fryer-checksEine Prüfung der Ölqualität und was entschieden wurde — gefiltert, gewechselt, belassen.
cooking-equipmentÖfen und Gargeräte, mit ihren Schwellen.
cooking-temperature-recordsEine Gartemperatur, von einer Person an einem Gargerät gemessen.
surface-analysesEin Oberflächenabstrich: was geprüft wurde, ob er bestanden wurde, und der Maßnahmenplan, falls nicht.
traceability-labelsEin gedrucktes Rückverfolgbarkeitsetikett. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten.
drive-filesEin Dokument, das im Drive des Restaurants abgelegt ist. Trägt eine Datei; siehe den Abschnitt zu Assets weiter unten.

Die Struktur jedes data-Objekts, Feld für Feld, steht im OpenAPI-Dokument, das aus dem laufenden Dienst erzeugt wird — es ist die eine Beschreibung, die nicht von dem abweichen kann, was ausgerollt ist.

Eine Collection durchlaufen

GET /v1/restaurants/{restaurantId}/collections/{collection}/records?limit=100
ParameterPflichtRegeln
limitnein1 bis 100. Standard ist 50.
cursorneinDer nextCursor der vorherigen Seite, wortgleich.

Hier gibt es keinen Zeitraum, anders als bei Lieferungen: Sie durchlaufen eine Collection, nicht ein Fenster daraus.

Ein Durchlauf ist ein konsistenter Snapshot. Die erste Seite fixiert die Position im Strom, und jede spätere Seite wird zu genau dieser Position beantwortet. Datensätze, die geschrieben werden, während Sie blättern, verschieben die Seiten unter Ihnen nicht und tauchen mitten im Durchlauf nicht auf — Sie sehen sie beim nächsten Durchlauf. Sortiert wird aufsteigend nach sequence.

Der Cursor ist an das Restaurant, die Collection und das Limit gebunden. Ändern Sie die Seitengröße mitten im Durchlauf, wird sie abgelehnt: Wählen Sie ein Limit und behalten Sie es für den ganzen Durchlauf.

async function* records({ apiKey, restaurantId, collection }) {
  const base = `https://api.backresto.com/v1/restaurants/${restaurantId}/collections/${collection}/records`;
  let cursor = null;

  do {
    const url = new URL(base);
    url.searchParams.set('limit', '100');
    if (cursor !== null) {
      url.searchParams.set('cursor', cursor);
    }

    const response = await fetch(url, {
      headers: { Authorization: `Bearer ${apiKey}` }
    });
    if (!response.ok) {
      throw new Error(`BackResto ${response.status}`);
    }

    const page = await response.json();
    yield* page.data;
    cursor = page.nextCursor;
  } while (cursor !== null);
}

Eine Kopie aktuell halten

Es gibt heute keinen since-Parameter. Eine Auffrischung ist ein weiterer Durchlauf, und Sie gleichen ihn mit dem ab, was Sie schon halten:

  • Schlüsseln Sie auf id innerhalb einer Collection und überschreiben Sie, wenn die sequence, die Sie bekommen, höher ist als die gespeicherte.
  • Beachten Sie Löschmarkierungen. deleted: true ist die Anweisung zu löschen; sie anzuwenden ist der einzige Weg, auf dem Ihre Kopie aufhört auseinanderzulaufen.
  • Durchlaufen Sie zu einer vernünftigen Stunde. Eine vollständige Collection mit limit=100 ist für ein einzelnes Restaurant eine Handvoll Anfragen, und das Budget sind 300 pro Minute — aber mehrere hundert Restaurants in derselben Cron-Minute sind eine Spitze, die Ihnen gehört.

Wenn ein inkrementeller Cursor ändern würde, was Sie bauen können, sagen Sie es. Es ist eine kleine Änderung an einem Strom, der ohnehin geordnet ist — der Grund, warum es ihn nicht gibt, ist, dass ihn bisher niemand gebraucht hat.

Assets

Drei Collections tragen eine Datei: cleaning-task-pictures, traceability-labels und drive-files. Der Datensatz benennt sie in data.asset; die Bytes kommen vom Assets-Endpunkt.

GET /v1/restaurants/{restaurantId}/collections/{collection}/records/{recordId}/assets
{
  "data": [
    {
      "id": "a17c93be4f02",
      "contentType": "application/pdf",
      "byteLength": 184320,
      "sha256": "9f2a…",
      "uploadedAt": "1789000000000",
      "url": "https://…?X-Amz-Signature=…",
      "urlExpiresAt": "1789000900000"
    }
  ]
}

Er antwortet weitgehend so wie die Lieferfotos: eine Liste signierter URLs, jede fünfzehn Minuten gültig, jede mit ihrer eigenen Autorisierung, sodass der Download keinen Header braucht. Zwei Unterschiede sollte man kennen.

Das sind Dateien, nicht nur Bilder. Ein Reinigungsbeleg ist ein Foto, aber ein Rückverfolgbarkeitsetikett oder eine Drive-Datei kann ein PDF, eine CSV, eine Tabelle oder ein Word-Dokument sein — lesen Sie contentType, statt ein Bild anzunehmen, und benennen Sie beim Import nicht alles in .jpg um.

sha256 ist da, damit Sie sich Arbeit sparen können. Es ist der Digest der Bytes: Passt er zu etwas, das Sie schon gespeichert haben, müssen Sie es nicht erneut herunterladen.

Es erscheinen nur Assets, deren Upload abgeschlossen und geprüft ist — eines, das noch unterwegs ist, fehlt, statt kaputt zu sein, und dasselbe gilt, sobald der übergeordnete Datensatz gelöscht ist.

Der Rest des Rats ist identisch, und es lohnt sich, ihn zu wiederholen, weil der Fehlschlag leise ist: Holen Sie die Bytes jetzt, speichern Sie die Bytes statt des Links, und rufen Sie den Endpunkt erneut auf, wenn Sie eine frische URL brauchen.

Die Fehler, die Ihnen begegnen werden

403 — Ihr Schlüssel erreicht das Restaurant, aber nicht mit der Berechtigung dieser Collection. Der Collections-Endpunkt ist der billige Weg herauszufinden, welche er hat.

404 — überhaupt keine Zuweisung für dieses Restaurant, oder es gibt diesen Datensatz nicht. Beides ist absichtlich nicht unterscheidbar.

400 — eine Collection außerhalb der vierundzwanzig oben, oder ein Cursor, der nicht zu diesem Restaurant, dieser Collection und diesem Limit gehört.

Alle drei sind Problemdokumente mit einer requestId.

Zuletzt aktualisiert am 2026-09-19.