Skip to main content
Version: 0.17

Updating data

Data in a source can be updated by applying a transform, which consists of one or more operations. Transforms must be applied atomically—all operations succeed or fail together.

Operations​

Operations each represent a single change to a record or relationship (e.g. adding a record, updating a field, deleting a relationship, etc.).

The base Operation interface consists of:

  • op - a string identifying the type of operation
  • options - (Optional) a RequestOptions object

The other members of an Operation are specific to the op. The following standard record-specific operations are defined in @orbit/records:

interface AddRecordOperation extends Operation {
op: 'addRecord';
record: InitializedRecord;
}

interface UpdateRecordOperation extends Operation {
op: 'updateRecord';
record: InitializedRecord;
}

interface RemoveRecordOperation extends Operation {
op: 'removeRecord';
record: RecordIdentity;
}

interface ReplaceKeyOperation extends Operation {
op: 'replaceKey';
record: RecordIdentity;
key: string;
value: string;
}

interface ReplaceAttributeOperation extends Operation {
op: 'replaceAttribute';
record: RecordIdentity;
attribute: string;
value: unknown;
}

interface AddToRelatedRecordsOperation extends Operation {
op: 'addToRelatedRecords';
record: RecordIdentity;
relationship: string;
relatedRecord: RecordIdentity;
}

interface RemoveFromRelatedRecordsOperation extends Operation {
op: 'removeFromRelatedRecords';
record: RecordIdentity;
relationship: string;
relatedRecord: RecordIdentity;
}

interface ReplaceRelatedRecordsOperation extends Operation {
op: 'replaceRelatedRecords';
record: RecordIdentity;
relationship: string;
relatedRecords: RecordIdentity[];
}

interface ReplaceRelatedRecordOperation extends Operation {
op: 'replaceRelatedRecord';
record: RecordIdentity;
relationship: string;
relatedRecord: RecordIdentity | null;
}

Transforms​

The Transform interface has the following members:

  • id - a string that uniquely identifies the transform
  • operations - an instance or array of Operation objects
  • options - an optional object that represents options that can influence how a transform is processed

Although transforms can be created "manually", you'll probably find it easier to use a builder function that returns a transform.

To use a transform builder, pass a function into a source's method that expects a transform, such as update or push. A TranformBuilder that's compatible with the source should be applied as an argument. You can then use this builder to create one or more operations.

For instance, here's how you might update a memory source with a single record:

const earth = {
type: 'planet',
id: 'earth',
attributes: {
name: 'Earth'
}
};

memory.update((t) => t.addRecord(earth));

To perform more than one operation in a single transform, just return an array of operations:

memory.update((t) => [t.addRecord(earth), t.addRecord(jupiter)]);

Standard transforms​

You can use the standard @orbit/data transform builder as follows:

// Adding a new record
memory.update((t) =>
t.addRecord({
type: 'planet',
id: 'earth',
attributes: {
name: 'Earth'
}
})
);

// Updating a record
memory.update((t) =>
t.updateRecord({
type: 'planet',
id: 'earth',
attributes: {
name: 'Earth',
classification: 'terrestrial',
atmosphere: true
}
})
);

// Removing a record
memory.update((t) => t.removeRecord({ type: 'planet', id: 'earth' }));

// Replacing a key
memory.update((t) =>
t.replaceKey({ type: 'planet', id: 'earth' }, 'remoteId', 'abc123')
);

// Replacing an attribute
memory.update((t) =>
t.replaceAttribute(
{ type: 'planet', id: 'earth' },
'classification',
'gaseous'
)
);

// Adding a member to a to-many relationship
memory.update((t) =>
t.addToRelatedRecords({ type: 'planet', id: 'jupiter' }, 'moons', {
type: 'moon',
id: 'io'
})
);

// Removing a member from a to-many relationship
memory.update((t) =>
t.removeFromRelatedRecords({ type: 'planet', id: 'jupiter' }, 'moons', {
type: 'moon',
id: 'io'
})
);

// Replacing every member of a to-many relationship
memory.update((t) =>
t.replaceRelatedRecords({ type: 'planet', id: 'jupiter' }, 'moons', [
{ type: 'moon', id: 'io' },
{ type: 'moon', id: 'europa' }
])
);

// Replacing a to-one relationship
memory.update((t) =>
t.replaceRelatedRecord({ type: 'planet', id: 'jupiter' }, 'solarSystem', {
type: 'solarSystem',
id: 'ourSolarSystem'
})
);

Transform options​

Options can be added to transforms to provide processing instructions to particular sources and to include metadata about transforms.

For example, the following transform is given a label and contains instructions for the source named remote:

memory.update(
(t) =>
t.updateRecord({
type: 'planet',
id: 'earth',
attributes: {
name: 'Earth',
classification: 'terrestrial',
atmosphere: true
}
}),
{
label: 'Update planet Earth',
sources: {
remote: {
timeout: 100000
}
}
}
);

A label can be useful for providing an understanding of actions that have been queued for processing.

The sources: { ${sourceName}: sourceSpecificOptions } pattern is used to pass options that only a particular source will understand when processing a transform. In this instance, we're telling our remote source to use a custom timeout when performing this particular update.

It is possible to pass different options to each operation in the transform.

memory.update((t) => [
t
.addRecord({
type: 'planet',
attributes: {
name: 'Earth'
}
})
.options({ timeout: 1000 }),
t
.addRecord({
type: 'planet',
attributes: {
name: 'Jupiter'
}
})
.options({ timeout: 2000 })
]);