Basics
Features
Best Practices
Addon Development
Related Docs
Dark Mode

#Collections

Under the hood, Collections are called Datastores and Entries are called DataItems. "Datastore" and "DataItem" will be used in this documentation when talking about API access while "Collection" and "Entry" will be used when talking about user interface.

#Datastore Schema

The schema for a Datastore is shown below. We will go into what each property does here.

{

  // uneditable properties
  _id: '639cf545ea990f22e697185e',
  parentVersionId: '639cf555ea990f22e6971860',
  managedByAddonId: '639b8f837f5fb8a39404280a',
  createdAt: 1671250792833,
  updatedAt: 1671250878818,

  // basic properties
  name: 'Vehicle',
  description: 'Vehicles for sale',
  icon: 'directions_car',

  // general properties
  isEnabled: true,
  isGlobal: false,
  isGlobalSelectable: false,
  isInNav: false,
  isAddonDataItemCreateEnabled: false,
  isAddonDataItemDeleteEnabled: false,

  // fields
  fields: [],

  // form layout
  tabs: [],
  sections: [],

  // display fields
  displayField: 'displayTitle',
  displayFieldSecondary: '',
  displayFieldExtra: '',
  displayFieldContent: 'description',
  displayFieldImage: 'images',

  // sorting fields
  sortBy: 'listedAt',
  sortOrder: 'desc',

  // archive fields
  archiveField: 'listedAt',
  archiveAfterDays: 90,

  // build-time properties
  functions: [],
  buildScript: '',

}

#Uneditable Properties

NameDescription
_idAssigned by MercuryCMS when the Datastore is created
parentVersionIdUsed by MercuryCMS to manage revision history
createdAtJavaScript Date the Datastore was created
updatedAtJavaScript Date the Datastore was last modified
managedByAddonIdAll of your created Datastores have this automatically added so they can be removed when the Addon is uninstalled

#Basic Properties

NameDescription
nameAll Datastores must have a name. Use something that won't conflict with Datastores the user has created
descriptionOptional, but good form to include a description of what the Datastore contains
iconMaterial Icons icon, for example: star

#General Properties

NameDescription
isEnabledSet this to false to disable the Datastore. The user can override this value
isGlobalWhen true, a single DataItem is used for the whole site
isGlobalSelectableWhen true, the user can select which DataItem to use from the available DataItems in the Datastore. When false, there should be a single DataItem provided
isInNavWhen true will show this Datastore in the Sidebar
isAddonDataItemCreateEnabledWhen true, the user can manually create DataItems in this Datastore. See User Modification below

#Fields

An array of Fields. Fields have common properties and properties unique to that type. Each type is explained below. All properties other than name and type have a default value and can be omitted when creating a Field or Datastore. See Collection Fields for more information.

#Common Field Properties

These properties are available on most Field types.

NameDescription
nameThis is the display name of the Field. For example "Full Name"
descriptionThis is used below the Field's label on forms. For example: "Your full name including first and last name."
typeThe type of Field. This can be one of "string" (Text), "number", "date", "color", "richText", "media", "asset", "boolean", "reference" (Relation), "enum" (Enumerable), "array", or "object"
initialValueThis is the value that will initially be in the Field when the user creates a new Entry in the interface. This is not the default value if no value is provided and has no effect when creating DataItems through the API. Supported by all types but array and object. See Initial Values for more information
computedValueThe computed value that will automatically update when the user types into the form when editing the Entry. This does not compute when setting values through the API. Supported by all types but array and object. See Computed Values for more information
minLength, maxLengthEither "" to disable, or a number indicating the min/max length/items. This applies to string, richText, and array
isReadonlyWhen true, the Field's value cannot be edited by the user in the form. This is useful for showing the user a computed value. This is also useful when used in conjunction with Shared Fields as described below in Importing From External APIs
isRequiredWhen true the form will require the user to fill out this Field before saving
isUniqueInDatastoreWhen true, no two DataItems may have the same value for this Field. Supported by all types but array, boolean, and object

#String (Text)

Strings have the following special properties they can optionally specify.

NameDescription
stringTypeOne of "" (General), "email", "fullName", "materialIcon", "phone"
formatAn array of formats to apply to the string when the user is editing the form. They apply in order. (see below)

Formats have the following properties. Note that only type is required for all but the "replace" format type.

NameDescription
typeOne of "camelcase", "kebabcase", "lowercase", "snakecase", "studlycase", "uppercase", "alphanumeric" (Letters and Numbers Only), "alpha" (Letters Only), "capitalize", "capitalizewords", "replace", "reverse", or "trim". Required
matchThe value to replace. This will be used to create a RexExp match. Required if type is "replace"
matchOptionsEither "gi" or "g". Required if type is "replace"
replacementThe replacement text. Required if type is "replace"

#Number

Numbers have the following special properties they can optionally specify.

NameDescription
minValue, maxValueEither "" to disable, or a number indicating the min/max value. This only affects what the user can enter in the form

#Date

Dates have the following special properties they can optionally specify.

NameDescription
dateTypeOne of "" (Date YYYY-MM-DD), "time" (Time HH:mm), "datetime" (JavaScript Date)

You must provide date values for this Field in the format you specify with dateType. For example:

'2023-02-03'  // dateType: ''
'23:49'       // dateType: 'time' (always 24-hour format)
1678003648592 // dateType: 'datetime' (JavaScript Date)

#Rich Text

Rich Text Fields have the following special properties they can optionally specify.

NameDescription
assetUploadPathAn optional path in Assets the user can upload files to. Cannot include spaces or special characters. The directory will automatically be created if it doesn't exist. Ex: /documents/rental_agreements
mediaUploadPathAn optional path in the Media Library the user can upload files to. Can include spaces. The directory will be automatically created if it doesn't exist. Ex: "Property Images/Interior"

#Media

Media Fields have the following special properties they can optionally specify.

NameDescription
mediaTypeOne of "" (Any), "image", "video", "image/video", or "audio"
mediaUploadPathAn optional path in the Media Library the user can upload files to. Can include spaces. The directory will be automatically created if it doesn't exist. Ex: "Property Images/Interior"

#Asset

Asset Fields have the following special properties they can optionally specify.

NameDescription
assetTypeA comma-separated list of accepted MIME types and/or file extensions. For example: "image/*, .pdf, .zip, application/msword"
assetUploadPathAn optional path in Assets the user can upload files to. Cannot include spaces or special characters. The directory will automatically be created if it doesn't exist. Ex: /documents/rental_agreements

#Enum (Enumerable)

Enum Fields have the following special properties they can optionally specify.

NameDescription
enumTypeOne of "" (String), or "number"
enumValuesAn array of selectable enumerable options with the format {name: 'Some Name', value: 'someValue'}.

You can also specify isEnumValueCreateDisabled on the Field to prevent the user from creating their own enumValues.

#Reference (Relation)

Reference Fields have the following special properties. referenceTo is required.

NameDescription
referenceToRequired for reference fields. This is the _id of the Datastore to reference to

#Array

Array Fields must specify the field property.

NameDescription
fieldThe description of what the Array's Field should be. This is another field object with all of the same properties this Field has (name, type, etc.)

For example, an array of images might look like this:

{
  name: 'Images',
  type: 'array',
  minLength: 1,
  maxLength: 20,
  field: {
    type: 'media',
    mediaType: 'image',
    uploadPath: 'Blog Post Images',
  },
}

#Object

Object Fields can optionally specify many of the same properties the Datastore can. Specifically tabs, sections, fields, displayField, displayFieldSecondary, displayFieldExtra, displayFieldContent, and displayFieldImage.

#Form Layout

Datastores and Object Fields can define a tabs and sections array. Tabs are currently under development, and thus, do nothing.

The form is described by a list of sections. These sections are shown vertically in the order they're defined in the array. Any Fields not specified in the form's sections are hidden. Here's an example:

sections: [
  {
    rows: [1, 1, 1],
    items: [
      {type: 'field', field: 'title'},
      {type: 'field', field: 'excerpt'},
      {type: 'field', field: 'body'},
    ]
  },
  {
    rows: [1, 1],
    items: [
      {type: 'field', field: 'metaTitle'},
      {type: 'field', field: 'metaDescription'},
    ]
  },
  {
    location: 'sidebar',
    rows: [2, 1, 1, 1, 1, 1],
    items: [
      {type: 'field', field: 'isFeatured'}, // first row
      {type: 'field', field: 'isHidden'},
      {type: 'field', field: 'slug'}, // second row
      {type: 'field', field: 'featuredImage'}, // third row
      {type: 'field', field: 'publishedAt'}, // fourth row
      {type: 'field', field: 'category'}, // fifth row
      {type: 'field', field: 'author'}, // sixth row
    ]
  },
]

Each section has a location which is either "main" or "sidebar" and defaults to "main". It then has an array called rows which defines how many items go into each row. You can see in the sidebar in this example that the isFeatured and isHidden Fields will be beside each other. It is recommended you do no more than 4 columns in the main sections and 2 columns in the sidebar.

Each item in the items array has a type. The only type available now is "field" which requires that you specify a field property which is the name of the field to show. This can be a Field from the Datastore or one of its Shared Fields. You can also have null as an item instead of an object for an empty space for that column.

In the future, additional item types will be added.

#Display Fields

NameDescription
displayFieldThe name of the Field to show in dropdowns and the Collection Entries screen. Typically something like Author's name or a computed property that combines some of the Fields like the House's full address or Vehicle's year, make, model, and VIN
displayFieldSecondaryThe name of the second-line display Field, like a Staff Member's title
displayFieldExtraThe name of the extra display Field with helpful data, like the Member's joinedAt Date
displayFieldContentThe name of the full text content Field to be previewed, like a Blog Post's body or Property's description
displayFieldImageThe name of the Media, Media Array, or Icon Field to be used for the image/icon

#Sorting Fields

NameDescription
sortByName of the Field to sort by in the interface, many times the same as the displayField. Also accepts _createdAt and _updatedAt
sortOrderEither asc or desc depending on which direction to sort

#Archive Fields

NameDescription
archiveFieldName of the Field to archive Entries in this Collection by. This must be a date field with a dateType of "" or "datetime". Leave blank to not archive. Also accepts _createdAt and _updatedAt
archiveAfterDaysNumber of days in the past this field must be to archive the Entry

#Build-time Properties

NameDescription
functionsArray of functions the Datastore or its DataItems should have available, explained below
buildScriptCode to run during the Build. See Collection Build Scripts

An example function is below. For more information, see Collection Functions

{
  type: 'instance',
  script: 'function doStuff(arg1, arg2) {\n  //do stuff\n}',
}

To make a function available on each DataItem (blogPost.getRelated()), use type: 'instance'. To make the function available on the Datastore (blogPostList.getRecentPosts()), use type: 'static'.

#User Modification

Collections created by your Addon behave slightly differently than Collections created by the user. The following rules apply:

  • Any Datastores your Addon creates will be automatically deleted when the Addon is uninstalled. This includes all of its DataItems including those created by the user.
  • The user cannot create or delete DataItems in any Datastore you create. You can specify isAddonDataItemCreateEnabled: true in the Datastore to allow the user to create and delete their own DataItems, but still not delete any created by your Addon. Specify isAddonDataItemDeleteEnabled: true in the Datastore to allow the user to delete DataItems created by your Addon.
  • The user cannot change the name of the Datastore or whether it is global, but can change whether it shows in the Sidebar, its Sorting Fields, Archive behavior, and its Display Fields.
  • The user can rearrange Fields, hide Fields from the form, and add their own, but not delete Fields you have created.
  • The user can add their own enumValues to enum Fields but not remove enumValues your Addon has created. To prevent the user from creating their own enumValues, set isEnumValueCreateDisabled: true on the field.
  • The user can modify the assetUploadPath and mediaUploadPath on Asset, Media, and Rich Text Fields.
  • The user can create their own Collection Functions but not remove ones you have created.

You must write your Addon in a way that allows for the user to extend your Addon's Datastore. They are expected to add their own fields as needed and it is part of MercuryCMS' design to allow the user to combine multiple Addons' data in their own Collections and Components. Do not write code that routinely destroys and recreates Datastore field schema or destroys all current schema when updating.

#Getting Datastores

You can get a list of all of your Datastores by calling cms.getDatastores(). This will return a full list of the Datastores your Addon has created and does not include any created by the user or other Addons.

// use static-cms-addon
import cms from 'static-cms-addon'

let datastores = await cms.getDatastores()
console.log(datastores.map(datastore => datastore.name)) // ['Blog Post', 'Category', 'Author']

// use axios
let response = await axios.get(`http://localhost:${cmsPort}/api/addon/${addonId}/datastore`)
console.log(response.data.datastores.map(datastore => datastore.name)) // ['Blog Post', 'Category', 'Author']

#Creating Datastores

You can create Datastores by using cms.upsertDatastore(). This function will look to see if a specific Datastore exists and update it, or create it if it doesn't exist. This will return the created Datastore including its assigned _id. You can then use that _id when creating Reference Fields.

You typically want to create Datastores right when your Addon starts. Updating Datastores will this function will not overwrite user-managed settings like any functions they have created, custom fields they've added, etc.

let datastores = await cms.getDatastores()
let blogCategoryDatastore = datastores.find(datastore => datastore.name == 'Blog Category')
let blogAuthorDatastore = datastores.find(datastore => datastore.name == 'Blog Author')

let blogPostDatastoreDef = {
  name: 'Blog Post',
  description: 'Blog Posts imported from WordPress',
  icon: 'edit',
  displayField: 'title',
  displayFieldSecondary: '',
  displayFieldContent: 'body',
  displayFieldImage: 'featuredImage',
  sortBy: 'publishedAt',
  sortOrder: 'desc',
  fields: [
    {name: 'Title'},
    {name: 'Excerpt'},
    {name: 'Body', type: 'richText'},
    {name: 'Meta Title'},
    {name: 'Meta Description'},
    {name: 'Is Featured', type: 'boolean'},
    {name: 'Is Hidden', type: 'boolean'},
    {name: 'Slug', computedValue: '{{ this.title }}', format: [{type: 'kebabcase'}]},
    {name: 'Featured Image', type: 'media', mediaType: 'image'},
    {name: 'PublishedAt', type: 'date', dateType: 'datetime'},
    {name: 'Category', type: 'reference', referenceTo: blogCategoryDatastore._id},
    {name: 'Author', type: 'reference', referenceTo: blogAuthorDatastore._id}
  ],
  sections: [
    {
      rows: [1, 1, 1, 1, 1],
      items: [
        {type: 'field', field: 'title'},
        {type: 'field', field: 'excerpt'},
        {type: 'field', field: 'body'},
      ]
    },
    {
      rows: [1, 1],
      items: [
        {type: 'field', field: 'metaTitle'},
        {type: 'field', field: 'metaDescription'},
      ]
    },
    {
      location: 'sidebar',
      rows: [2, 1, 1, 1, 1, 1],
      items: [
        {type: 'field', field: 'isFeatured'},
        {type: 'field', field: 'isHidden'},
        {type: 'field', field: 'slug'},
        {type: 'field', field: 'featuredImage'},
        {type: 'field', field: 'publishedAt'},
        {type: 'field', field: 'category'},
        {type: 'field', field: 'author'},
      ]
    },
  ]
}

// use static-cms-addon
import cms from 'static-cms-addon'

let blogPostDatastore = await cms.upsertDatastore({name: 'Blog Post'}, blogPostDatastoreDef, 'Added Meta Title and Meta Description.')
console.log(blogPostDatastore._id) // "639e58c84b851d93f27dc830"

// use axios
let response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/datastore`, blogPostDatastoreDef)
console.log(response.data.datastore._id) // "639e58c84b851d93f27dc830"

The first argument to upsertDatastore() is the fields to match against. This should typically just be name. The second argument is the Datastore definition, and the third argument is an optional Publish Comment. You can use this to provide information about what changed in the Datastore. This will not be used if the Datastore is new, and will only be shown if the Datastore you are upserting has actually changed.

When specifying reference fields, instead of using the reference Datastore's _id, you can instead use their camelCase name. For example, to reference a Datastore named "Blog Author", you could use {name: 'Author', type: 'reference', referenceTo: 'blogAuthor'}. The reference Datastore will still need to first exist before you can reference to it.

You can also load the Datastore from a zip or folder using cms.loadDatastorePackage().

let componentDef = cms.loadDatastorePackage('components/myComponent.datastore.zip').datastore
let component = await cms.upsertDatastore(componentDef)

#Deleting Datastores

You can use cms.deleteDatastores() to delete Datastores your Addon has created. This will also delete all of its DataItems.

// use static-cms-addon
import cms from 'static-cms-addon'

let datastores = await cms.getDatastores()
let datastore = datastores.find(datastore => datastore.name == 'My Datastore')
await cms.deleteDatastores([datastore._id])

// use axios
let response = await axios.get(`http://localhost:${cmsPort}/api/addon/${addonId}/datastore`)
let datastore = response.data.datastores.find(datastore => datastore.name == 'My Datastore')
await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/datastore/delete`, {datastoreIds: [datastore._id]})

#DataItems

DataItem schema is shown below:

{

  // uneditable properties
  _id: '639e8eb64b851d93f27dc843',
  parentVersionId: '639e8ef54b851d93f27dc845',
  managedByAddonId: '639b8f837f5fb8a39404280a',
  createdAt: 1671335606730,
  updatedAt: 1671335669405,

  // datastore that determines dataitem fields
  datastoreId: '639e8eb64b851d93f27dc843',

  // general properties
  isEnabled: true,

  // field metadata
  overriddenFields: [],

  // field values
  fields: {},

}

#Uneditable DataItem Properties

NameDescription
_idAssigned by MercuryCMS when the DataItem is created
parentVersionIdUsed by MercuryCMS to manage revision history
createdAtJavaScript Date the DataItem was created
updatedAtJavaScript Date the DataItem was last modified
managedByAddonIdAll of your created DataItems have this automatically added so they can be removed when the Addon is uninstalled

#General Properties

NameDescription
isEnabledSet this to false to disable the DataItem. The user can override this field

#Field Metadata

These properties are automatically managed by the interface. For example, when the user clicks the "Override" switch and enters their own value in place of a Computed Value, MercuryCMS adds the name of the Field to overriddenFields.

NameDescription
overriddenFieldsA list of Computed Fields that have been overridden by the user. Ex: ["slug"]

#DataItem Fields

The fields object contains the values of the Fields. For example:

fields: {
  title: 'My Blog Post',
  excerpt: 'My post\'s excerpt',
  body: '# Some Heading\n\nSome text',
  metaTitle: 'My Blog Post',
  metaDescription: 'A post I made',
  isFeatured: true,
  isHidden: false,
  slug: 'my-blog-post',
  featuredImage: '639e92b24b851d93f27dc847',
  keywords: ['post', 'blog', 'my', 'demo'],
  publishedAt: 1671336660864,
  category: '639e92f14b851d93f27dc848',
  author: '639e92f84b851d93f27dc84a',
  review: {
    overriddenFields: [],
    fields: {
      title: 'My Review',
      stars: 4,
      body: 'This blog post is pretty good.',
      reviewer: {
        overriddenFields: [],
        fields: {
          name: 'John Doe',
          email: 'johndoe@example.com',
        }
      }
    }
  }
}

As you can see, all values are stored in their native type (string Fields are strings, number Fields are numbers, array Fields are arrays, etc. The only special Field type is object, which has its own fields property and Field metadata (overriddenFields etc.) When you retrieve a DataItem from the API, you will need to account for this format.

#Getting DataItems

You can get DataItems by using cms.getDataItem() and cms.getDataItems(). You can optionally provide a match key to getDataItems(). This is a Mongo-compatible query which supports all directives like $in and $ne. datastoreId is automatically added to your match query based on the path parameters in the request. You can also specify an array of dataItemIds rather than having to use a cumbersome combination of $in and $oid.

// use static-cms-addon
import cms from 'static-cms-addon'

let authorDataItem = await cms.getDataItem({datastoreId: authorDatastore._id, dataItemId: '639ea4aa4b851d93f27dc84f'})
let allBlogPostDataItems = await cms.getDataItems({datastoreId: blogPostDatastore._id})
let authorBlogPostDataItems = await cms.getDataItems({datastoreId: blogPostDatastore._id, match: {'fields.author': authorDataItem._id}})
let categoryDataItems = await cms.getDataItems({datastoreId: categoryDatastore._id, dataItemIds: ['639ea62e4b851d93f27dc851', '639ea6394b851d93f27dc857']})

// use axios
let response = await axios.get(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${authorDatastore._id}/639ea4aa4b851d93f27dc84f`)
let authorDataItem = response.data.dataItem

response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${blogPostDatastore._id}/list`)
let allBlogPostDataItems = response.data.dataItems

response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${blogPostDatastore._id}/list`, {match: 'fields.author': authorDataItem._id})
let authorBlogPostDataItems = response.data.dataItems

response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${categoryDatastore._id}/list`, {dataItemIds: ['639ea62e4b851d93f27dc851', '639ea6394b851d93f27dc857']})
let categoryDataItems = response.data.dataItems

#Creating DataItems

You can create DataItems by calling cms.upsertDataItems(). You can either pass a DataItem that already contains fields, or you can pass raw: true to the request and pass in DataItems in the same format you would see them when Templating. This is very useful when pulling the data from an external API so you don't have to parse the raw objects yourself.

Note: You never need to set datastoreId on a DataItem. This is always done automatically by MercuryCMS.

let rawBlogPosts = [
  {
    title: 'My Blog Post',
    excerpt: 'My post\'s excerpt',
    body: '# Some Heading\n\nSome text',
    metaTitle: 'My Blog Post',
    metaDescription: 'A post I made',
    isFeatured: true,
    isHidden: false,
    slug: 'my-blog-post',
    featuredImage: '639e92b24b851d93f27dc847',
    keywords: ['post', 'blog', 'my', 'demo'],
    publishedAt: 1671336660864,
    category: '639e92f14b851d93f27dc848',
    author: '639e92f84b851d93f27dc84a',
    review: {
      title: 'My Review',
      stars: 4,
      body: 'This blog post is pretty good.',
      reviewer: {
        name: 'John Doe',
        email: 'johndoe@example.com',
      }
    }
  }
]

// use static-cms-addon
import cms from 'static-cms-addon'

let blogPostDataItems = await cms.upsertDataItems({
  datastoreId: blogPostDatastore._id,
  match: ['fields.slug'],
  dataItems: rawBlogPosts,
  publishComment: 'Import from my website',
  raw: true,
})
console.log(blogPostDataItems[0].fields.review.fields.title) // "My Review"

// use axios
let response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${blogPostDatastore._id}`, {match: ['fields.slug'], dataItems: rawBlogPosts, publishComment: 'Import from my website', raw: true})
console.log(response.data.dataItems[0].fields.review.fields.title) // "My Review"

The match option allows you to provide an array of values that will be checked on each DataItem. In the above example, each Blog Post being passed in will be matched against an existing Blog Post based on its slug. You can provide multiple values to match against.

The Publish Comment is optional. You can use this to provide information about what changed in the DataItem. This will not be used if the DataItem is new, and will only be shown if the DataItem you are upserting has actually changed.

You can also create individual DataItems using cms.upsertDataItem(). In the following example, an object is used for match, but this call also supports simply passing ['fields.slug'] as well.

let blogPostDataItem = await cms.upsertDataItem({
  datastoreId: blogPostDatastore._id,
  match: {'fields.slug': blogPostDataItem.slug},
  dataItems: rawBlogPost,
  publishComment: 'Import from my website',
  raw: true,
})
console.log(blogPostDataItem.fields.review.fields.title) // "My Review"

// use axios
let response = await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${blogPostDatastore._id}`, {match: {'fields.slug': rawBlogPost.slug}, dataItems: [rawBlogPost], publishComment: 'Import from my website', raw: true})
console.log(response.data.dataItems[0].fields.review.fields.title) // "My Review"

#Deleting DataItems

You can delete DataItems by using cms.deleteDataItems(). You must provide a match key. This is a Mongo-compatible query which supports all directives like $in and $ne. datastoreId is automatically added to your match query based on the path parameters in the request. You can also specify an array of dataItemIds rather than having to use a cumbersome combination of $in and $oid, or an array of notDataItemIds rather than $nin and $oid.

// use static-cms-addon
import cms from 'static-cms-addon'

let authorDataItems = await cms.getDataItems({datastoreId: authorDatastore._id, match: {'fields.name': 'John Doe'}})
let authorDataItem = authorDataItems[0]
await cms.deleteDataItems({datastoreId: blogPostDatastore._id, match: {'fields.author': authorDataItem._id}})

// use axios
await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${blogPostDatastore._id}/delete`, {match: {'fields.author', authorDataItem._id}})

#Enabling and Disabling Data Items

You can enable and disable DataItems using cms.enableDataItems() and cms.disableDataItems().

// use static-cms-addon
import cms from 'static-cms-addon'

await cms.enableDataItems({datastoreId: storeItemDatastore._id, match: {'fields.onSale': true}, publishComment: 'Listing sale items.'})
await cms.disableDataItems({datastoreId: storeItemDatastore._id, match: {'fields.sourceId': 863}, publishComment: 'Disabling discount supplier.'})

// use axios
await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${storeItemDatastore._id}/enable`, {match: {'fields.onSale': true}, publishComment: 'Listing sale items.'})
await axios.post(`http://localhost:${cmsPort}/api/addon/${addonId}/dataitem/${storeItemDatastore._id}/disable`, {match: {'fields.sourceId': 863}, publishComment: 'Disabling discount supplier.'})

You can also specify dataItemIds or notDataItemIds. This is useful when importing from an external source and disabling all DataItems except the ones you just imported. Do this rather than deleting all of the DataItems not found, so the user can manually enable them if they wish, and their overrides won't be destroyed.

#Importing From External APIs

One of the most common use-cases for Addons is to pull data from an external source and make them available in MercuryCMS for Templating. Because of this, there are a number of built-in features that allow you to make this as simple as possible. Let's do a simple example first, then build on it.

import cms from 'static-cms-addon'

let storeItemDatastore = await cms.upsertDatastore({name: 'Store Item'}, {
  name: 'Store Item',
  description: 'Your Store Items from your Example.com Store',
  icon: 'shopping_bag',
  fields: [
    {name: 'ID', type: 'number', isReadonly: true, isManagedByAddon: true},
    {name: 'Price', type: 'number', isReadonly: true, isManagedByAddon: true},
    {name: 'Name', isReadonly: true, isManagedByAddon: true},
  ],
  sections: [
    {
      rows: [1],
      items: [
        {type: 'field', field: 'name'},
      ]
    },
    {
      location: 'sidebar',
      rows: [1, 1],
      {type: 'field', field: 'id'},
      {type: 'field', field: 'price'},
    }
  ]
})

let response = await axios.get('https://example.com/api/items')
let storeItems = response.data.storeItems
console.log(storeItems)
// [
//   {id: 736273, price: 999, name: 'Coffee Mug'},
//   {id: 137823, price: 1299, name: 'Hat'},
//   {id: 321232, price: 2499, name: 'T-Shirt'},
// ]

let dataItems = await cms.upsertDataItems({
  datastoreId: storeItemDatastore._id,
  match: ['id'],
  dataItems: storeItems,
  raw: true
})
await cms.disableDataItems({datastoreId: storeItemDatastore._id, notDataItemIds: dataItems.map(dataItem => dataItem._id)})

In this example, you can see how easy it is for us to update the Store Item Collection and disable unused items. The user can use this data via storeItemList when Templating and we can provide some Components for the user to quickly get started.