DataSync data operations

Every DataSync resource, entities, relationships, users, channels, and memberships, supports the same operation set: list, get, create, replace, partial update, and delete. All of these operations require Access Manager authorization. Refer to DataSync access control for how that works.

Every operation maps to a Core REST API endpoint:

EntityRelationshipUserChannelMembership
ListGet entitiesGet relationshipsGet usersGet channelsGet memberships
GetGet entityGet relationshipGet userGet channelGet membership
CreateCreate entityCreate relationshipCreate userCreate channelCreate membership
ReplaceUpdate entityUpdate relationshipUpdate userUpdate channelUpdate membership
Partial updatePatch entityPatch relationshipPatch userPatch channelPatch membership
DeleteDelete entityDelete relationshipDelete userDelete channelDelete membership

For code examples, refer to Create entities and relationships and Query DataSync.

Creating objects​

Every create names the class the new object is an instance of. What else you must supply depends on the object kind:

CreatingYou must supply
An entityThe class name and class version
A relationshipBoth linked entity IDs, the class name, and the class version
A userThe class version. The class name defaults to User
A channelThe class version. The class name defaults to Channel
A membershipchannelId, userId, and the class version

You can optionally supply your own id, a status, and the payload. If you don't supply an id, the server generates one. Supplying an id that already exists is rejected with a 409 Conflict.

Reading objects​

Getting an object by ID returns its system fields and its payload. Projection filtering applies to payload and status only: the remaining system fields are always returned in full. Refer to Projections.

Expired objects are not returned by reads. Eventually consistent search (filter) can return an object whose expiresAt has passed, so check expiresAt on results from that tier.

Updating objects​

DataSync supports two ways to update an object: replace and partial update.

Replace​

Replace is a PUT. It sends the new state for every payload field visible within the token's resolved projection, including the class version. Payload fields outside that projection are left untouched. For a class with no custom projections, replace behaves exactly like sending the full object.

Two things to watch in a replace body:

  • The class version isn't a passive echo. Sending a different existing version of the same class re-points the object at that version. A version whose base Global class differs from the object's own is rejected with a 400.
  • status is protected by its declared projections. If the resolved projection includes /status, omitting status from a replace body sets it to null. If the resolved projection excludes /status, changing it is rejected with a 403.

Partial update​

Partial update is a PATCH. It uses JSON Patch (RFC 6902) with add, remove, replace, move, copy, and test operations, addressed by JSON Pointer paths into the object. A pointer starts at the object, where payload is one field alongside the system fields, so the price on a product is /payload/price.

A partial update can only address three paths on the object:

  • /status
  • /entityClassVersion, or /relationshipClassVersion for relationships and memberships
  • /payload, or anything beneath it

Any other pointer is rejected with a 400 Bad Request. The test operation is the exception: it can target any pointer without modifying the document, which makes it a way to assert /eTag inline as part of the patch.

Optimistic concurrency with ETags​

Every entity and relationship carries an eTag that changes on every write. If-Match is an optional request header on every write endpoint. Omit it and the write applies unconditionally. Send the eTag you last read as If-Match to make the write conditional, or If-Match: * to require only that the object still exists. If the value no longer matches the object's current eTag, the write is rejected with a 412 Precondition Failed.

The pattern for safe concurrent writes:

  1. Read the object and note its eTag.
  2. Modify the fields you want to change.
  3. Write with If-Match: <eTag>. If the eTag still matches, the write succeeds with 200 OK and returns the new eTag.
  4. If the response is 412, re-read and retry.

If you're also subscribed to an object's events, use an incoming event as a signal to refresh your copy and its eTag before writing, rather than writing blindly.

Deleting objects​

Delete removes an object immediately. Like other writes, it respects If-Match, so a delete against a stale eTag is rejected with a 412. Deleting an entity also deletes every relationship linked to it, and each cascaded relationship delete publishes an event of its own.

A successful delete returns 200 OK with an empty body. Deleting an ID that doesn't exist returns a 404, so delete isn't idempotent.

Objects also expire automatically based on their class TTL. expiresAt is set once at creation and is not refreshed by updates. Refer to Schemas: data expiry (TTL).

Filtering​

DataSync offers two mutually exclusive filtering parameters: filter_fast for strongly consistent queries and filter for eventually consistent queries. Supplying both in one request is rejected with a 400.

Strongly consistent (filter_fast)Eventually consistent (filter)
Eligible propertiesDeclared with filtering mode simple or fullDeclared with filtering mode full
StorageSame store used for writesA separate search store
ConsistencyStrongly consistent; reflects completed writesEventually consistent; can briefly lag recent writes
Predicate countMaximum 10 by default, configurable per keysetNo predicate limit is enforced

Both parameters share the same expression language. Filter expressions use comparison operators (==, !=, <, >, <=, >=), pattern operators (LIKE, SLIKE, ILIKE), and logical operators (&&, ||, !). In filter and sort expressions, you always use a property's name, not its path.

Four fields every object carries natively are filterable and sortable without being declared on the class:

FieldFilters as
idstring
createdAtdatetime
updatedAtdatetime
statusstring
Declare filterable properties first

Beyond the four built-in fields, filtering only works on properties you've declared on the class, and only for data written after the property was declared. Data written earlier isn't retroactively indexed.

Listing generic entities requires an entity_class, and listing generic relationships requires a relationship_class. There's no way to list every entity on a keyset regardless of class.

Sorting and pagination​

List endpoints accept a sort parameter: a comma-separated list of property names, each optionally followed by :desc for descending order. Only declared properties are sortable, and which properties are sortable depends on the filtering tier: filter_fast supports simple and full properties, while filter supports full only.

List endpoints use cursor-based pagination. Page size ranges from 1 to 100, with a default of 20. Pagination is forward-only. To page forward, pass the response's next_cursor back as cursor on the next call, and stop when has_next is false.

Refer to Query DataSync for code examples.

Was this page useful?

Last updated on