Updating record is very similar to creating a new one from scratch, so make sure to read that guide and all the examples for more details.
When updating you can pass just the fields you want to change.
DatoCMS optionally supports optimistic locking. When updating an existing record, you can specify its current version within the appropriate meta
property. DatoCMS compares this version with the current version stored to ensure that a client doesn't overwrite a record that has since been updated. If the version changed in-between, DatoCMS would reject the update with a 422 STALE_ITEM_VERSION
error.
For more information, take a look at the Javascript examples.
Block records live inside of Modular Content and Structured Text fields, which require different data structures as their value: while Modular Content is simply an array of blocks, Structured Text accepts a Structured text document, which in turn might contain a number of block
nodes interspersed with textual content.
We’ll get to some detailed examples for both fields below, but the general idea that applies to both of them is that to preserve the atomicity property of ACID transactions, DatoCMS prohibits you from creating/editing/deleting blocks directly; you always need to add/edit/remove blocks by performing a create/update operation on the record that embeds them, so:
Depending on the endpoints you're going to use, the “fragment” representing the block can be simply its ID, or a full JSON API object:
Endpoint | HTTP request blocks representation | HTTP response blocks representation |
---|---|---|
GET /items | N/A | ID |
GET /items?nested=true | N/A | JSON:API resource object |
GET /item/:id | N/A | ID |
GET /item/:id?nested=true | N/A | JSON:API resource object |
POST /items | JSON:API resource object | ID |
PUT /items/:id | ID or JSON:API resource object | ID |
During an update operation, to reference a block:
id
);Before diving in, the key concepts to remember are the following:
en
and it
for the title
field, and only en
for the content
field.Above we said that when you perform an update operation to a record, you can pass just the fields you want to change, and not all of them. When dealing with localized content, the only exception to this rule is when you have a model that does not enforce a value for every locale, and you want to add or remove a locale on a specific record. Only in this case, you are required to pass all localized fields in the payload to enforce a locales coherence between all the localized fields.
Also, when you want to update the value for a specific localized field, you always need to pass an object containing the value for each locale you want to preserve, not just the one you might want to update, otherwise you’ll get an error.
If your API token can only manage a subset of locales, during an update operation you are required not to include in the payload any other locales. In this case, the content for the locales you cannot control and that you won’t pass will be kept intact by the system.
In the following table, you’ll find a recap showing the result of an update operation, depending on the API token permissions, the locales already present in a record, and the locales specified in the payload of the update operation itself:
Role can manage content in | Record contains localized fields with content in | Payload contains all localized fields with content in | Result for the update operation |
---|---|---|---|
English | English | English | English is updated. |
English, Italian | English | English, Italian | English is updated. Italian is added. |
English, Italian | English, Italian | English | English is updated. Italian is removed. |
English, Italian | English, Italian | English, Italian | English is updated. Italian is updated. |
English, Italian, French | English, Italian | English, French | English is updated. Italian is removed. French is added. |
English | English, Italian | English | English is updated. Italian is preserved. |
English, Italian | English, French | English, Italian | English is updated. French is preserved. Italian is added. |
English, Italian | English, French | Italian | English is removed. French is preserved. Italian is added. |
Below you’ll find some real-world examples related to locales addition/removal for a record.
Date of creation
Date of first publication
The ID of the current record version (for optimistic locking, see the example)
The new stage to move the record to
The record's model
The entity (account/collaborator/access token/sso user) who created the record. It must be an object with type
(e.g. 'account') and id
properties.
The following examples are available:
import { buildClient } from '@datocms/cma-client-node';async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const item = await client.items.update(itemId, {// we just pass the field that we want to changetitle: '[EDIT] My first blog post!',});console.log(item);}run();
import { buildClient, ApiError } from 'datocms-client';const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });async function updateRecordByIncrementingCounter(itemId) {// first we get the record we want to updateconst record = await client.items.find(itemId);try {// now we increment the counter value, passing the current version// to enable optimistic-lockingclient.items.update(itemId, {counter: record.counter + 1,meta: { current_version: record.meta.current_version },});console.log('Done!');} catch (e) {// if we get a STALE_ITEM_VERSION error, this means that the// the record changed in-between the find and update operations, so we have// to fetch the latest version of the record and try againif (e instanceof ApiError && e.findError('STALE_ITEM_VERSION')) {console.log('Stale version, retrying...');return updateRecordByIncrementingCounter();}throw e;}}await updateRecordByIncrementingCounter('1230982');
> node example.jsStale version, retrying...Stale version, retrying...Done!
Remember to pass the ID of the block record you want to update to buildBlockRecord()
. When updating a block record, you can pass just the fields you want to change.
const { buildClient, buildBlockRecord } = require('datocms-client');async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const blockModelId = '23455234';const blockRecordIdToUpdate = '565346547';const item = await client.items.update(itemId, {content: [// existing block record IDs'565346546','565346547',// new block recordbuildBlockRecord({item_type: { type: 'item_type', id: blockModelId },id: blockRecordIdToUpdate,text: 'updated text',}),],});console.log(item);}run();
const result = {type: 'item',id: '4235',title: 'Title',content: ['565346546', '565346547', '565346548'],meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};
You can reorder block records by changing their IDs position in the modular content field array.
import { buildClient } from '@datocms/cma-client-node';async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const item = await client.items.update(itemId, {// previous order was ['565346546', '565346547', '565346548']content: ['565346548', '565346547', '565346546'],});console.log(item);}run();
const result = {type: 'item',id: '4235',title: 'Title',content: ['565346548', '565346547', '565346546'],meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};
In this case, you must not pass an ID to buildBlockRecord
.
It's not required the presence of all the fields in the payload. If it's easier for you to include all of them, just pass null
, as it's a valid value for every field type.
When a field is not present in the payload, or its value is null
, then the field's default value will be used (if available).
const { buildClient, buildBlockRecord } = require('datocms-client');async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const blockModelId = '23455234';const item = await client.items.update(itemId, {content: [// new block record:buildBlockRecord({item_type: { type: 'item_type', id: blockModelId },text: 'new block record text',subtext: 'another text',}),// existing block record IDs:'565346546','565346547','565346548',],});console.log(item);}run();
const result = {type: 'item',id: '4235',title: 'Title',// block '565346549' has been added:content: ['565346549', '565346546', '565346547', '565346548'],meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};
To delete a block record, remove its ID from the modular content field array.
import { buildClient } from '@datocms/cma-client-node';async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const item = await client.items.update(itemId, {// previous value was ['565346546', '565346547', '565346548'], let's remove '565346547'content: ['565346546', '565346548'],});console.log(item);}run();
const result = {type: 'item',id: '4235',title: 'Title',// block '565346547' has been removed!content: ['565346546', '565346548'],meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};
If the 'All locales required?' option in a model is turned off, then its records do not need all environment's locales to be defined for localized fields, so you're free to add/remove locales during an update operation.
Suppose your environment's locales are English, Italian and German (['en', 'it', 'de']
) and the following record currently defines en
and it
locales on its localized fields (title
and content
):
const item = {id: '4235',// localized fieldtitle: {en: 'My title',it: 'Il mio titolo'},// localized fieldcontent: {en: 'Article content',it: 'Contenuto articolo',},// non-localized fieldvotes_count: 10,meta: { /* ... */ },item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },}
To add the de
locale to this record, you have to send an update request containing all localized fields. For each one of these, you must define the exiting locales plus the ones you want to add:
import { buildClient } from '@datocms/cma-client-node';async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const item = await client.items.update(itemId, {title: {en: 'My title',it: 'Il mio titolo',de: 'Mein Titel',},content: {en: 'Article content',it: 'Contenuto articolo',de: 'Artikelinhalt',},});console.log(item);}run();
const result = {type: 'item',id: '4235',title: {en: 'My title',it: 'Il mio titolo',de: 'Mein Titel',},content: {en: 'Article content',it: "Contenuto dell'articolo",de: 'Artikelinhalt',},votes_count: 10,meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};
If the 'All locales required?' option in a model is turned off, then its records do not need all environment's locales to be defined for localized fields, so you're free to add/remove locales during an update operation.
Let's suppose your environment's locales are English (en
), Italian (it
) and German (de
) and the following record currently defines en
and it
locales on its localized fields (title
and content
):
const item = {id: '4235',// localized fieldtitle: {en: 'My title',it: 'Il mio titolo'},// localized fieldcontent: {en: 'Article content',it: 'Contenuto articolo',},// non-localized fieldvotes_count: 10,meta: { /* ... */ },item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },}
To remove the it
locale from this record, you have to send an update request containing all localized fields. For each one fo these, you must define the exiting locales, except the ones you want to remove:
import { buildClient } from '@datocms/cma-client-node';async function run() {const client = buildClient({ apiToken: '<YOUR_API_TOKEN>' });const itemId = '4235';const item = await client.items.update(itemId, {title: {en: 'My title',},content: {en: 'Article content',},});console.log(item);}run();
const result = {type: 'item',id: '4235',title: {en: 'My title',},content: {en: 'Article content',},votes_count: 10,meta: {/* ... */},item_type: { type: 'item_type', id: '44' },creator: { type: 'access_token', id: '312' },};