Consuming Changes
Poll the change feed and continue without skipping records.
Use /v1/entities/changes to read entity changes. The filter parameter is
required.
curl --get "https://api.regbase.com/v1/entities/changes" \
--header "Authorization: Bearer $REGBASE_API_KEY" \
--data-urlencode "filter=jurisdiction:US.CA" \
--data-urlencode "page[limit]=100"Choose a feed strategy
Filters apply to the entity snapshot after the change. A narrow filter does not report an entity that stops matching it. Delete events also have an empty snapshot and usually do not match field filters.
Use a narrow filter only when missing departures and deletions is acceptable.
To maintain an accurate mirror, consume these two feeds with separate cursors:
-no:type
no:typeTogether they cover snapshots where type is present or missing, including
deletions. Apply your desired filter locally. Upsert matching entities and
remove entities that no longer match. If you poll the feeds independently,
store the latest recordedAt for each entity and ignore older changes.
Change metadata such as changeType, changedKeys, and recordedAt cannot be
used in the server-side filter.
Continue from a response
Each successful response contains:
data: the matching change recordslinks.next: a continuation URL when another page may be readymeta.cursor: the last safe position scanned by the server
Follow links.next exactly when it is present. This drains the current backlog
without rebuilding query parameters.
When links.next is absent, you are at the current head. Save meta.cursor,
put it in page[after], and poll again later. The cursor may advance even when
data is empty. Always save it after you successfully process the response.
const pollIntervalMs = 1_000;
async function pollChanges(filter) {
const initialUrl = new URL(
"https://api.regbase.com/v1/entities/changes",
);
initialUrl.searchParams.set("filter", filter);
initialUrl.searchParams.set("page[limit]", "100");
let url = initialUrl;
while (true) {
const response = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.REGBASE_API_KEY}`,
},
});
if (response.status === 429) {
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
continue;
}
if (!response.ok) {
throw new Error(`Regbase returned ${response.status}`);
}
const document = await response.json();
for (const change of document.data) {
await applyChange(change);
}
if (document.links.next) {
url = new URL(document.links.next);
continue;
}
url = new URL(document.links.self);
if (document.meta.cursor) {
url.searchParams.set("page[after]", document.meta.cursor);
}
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
}
}
await Promise.all([
pollChanges("-no:type"),
pollChanges("no:type"),
]);There is no required polling interval. Poll as often as your application needs.
Apply each change
The change resource is not an entity resource. Its id is the change ID, and
its attributes include change metadata. The entity ID and type are in
relationships.entity.data.
For create and update, either fetch
relationships.entity.links.related or reconstruct the entity:
function entityFromChange(change) {
const {
changeType,
changedKeys,
mergedInto,
recordedAt,
...attributes
} = change.attributes;
return {
type: change.relationships.entity.data.type,
id: change.relationships.entity.data.id,
attributes,
};
}Use changeType to update your local store:
createorupdate: replace the local entity with the reconstructed or fetched entity.delete: remove the ID inrelationships.entity.data.merge: remove that ID, then fetch the entity identified bymergedInto.
Change IDs and cursors are opaque. Compare or edit neither.