Add items to a datastore

Developing automations requires a paid plan. Don't have one? Join the Developer Program and provision a sandbox with access to all Slack features for free.

There are a few ways you can add information to a datastore. You can:

There's an important distinction between these methods! The put and bulkPut methods replace entire existing items, while the update method will only update the provided attributes for items. Be careful to not accidentally lose information when using the put and bulkPut methods.

Slack CLI commands
You can also add items to a datastore with the datastore put, datastore bulk-put, and datastore update Slack CLI commands. The datastore bulk-put command even supports importing data from a JSON Lines file.

Create or replace items with put and bulkPut

There are two methods for creating and replacing items in datastores:

They work quite similarly.

Example: Using the put method
    const putResp = await client.apps.datastore.put<
      typeof DraftDatastore.definition
    >({
      datastore: DraftDatastore.name,
      item: {
        id: draftId,
        created_by: inputs.created_by,
        message: inputs.message,
        channels: inputs.channels,
        channel: inputs.channel,
        icon: inputs.icon,
        username: inputs.username,
        status: DraftStatus.Draft,
      },
    });

Example: Using the bulkPut method
    const putResp = await client.apps.datastore.bulkPut<
      typeof DraftDatastore.definition
    >({
      datastore: DraftDatastore.name,
      items: [
        {
          id: draftId,
          created_by: inputs.created_by,
          message: inputs.message,
          channels: inputs.channels,
          channel: inputs.channel,
          icon: inputs.icon,
          username: inputs.username,
          status: DraftStatus.Draft,
        },
        {
          id: draftId2,
          created_by: inputs.created_by,
          message: inputs.message,
          channels: inputs.channels,
          channel: inputs.channel,
          icon: inputs.icon,
          username: inputs.username,
          status: DraftStatus.Draft,
        },
      ]
    });

That's the general format for each method - but let's look a full example to help connect the dots.

In this example, we create a custom function that creates and sends an announcement draft to a channel. Values for each of the datastore's attributes are passed in to create that announcement draft.

First is the custom function definition:

// /functions/create_draft/definition.ts
import { DefineFunction, Schema } from "deno-slack-sdk/mod.ts";

export const CREATE_DRAFT_FUNCTION_CALLBACK_ID = "create_draft";
/**
 * This is a custom function manifest definition which
 * creates and sends an announcement draft to a channel.
 *
 * More on defining functions here:
 * https://api.slack.com/automation/functions/custom
 */
export const CreateDraftFunctionDefinition = DefineFunction({
  callback_id: CREATE_DRAFT_FUNCTION_CALLBACK_ID,
  title: "Create a draft announcement",
  description:
    "Creates and sends an announcement draft to channel for review before sending",
  source_file: "functions/create_draft/handler.ts",
  input_parameters: {
    properties: {
      created_by: {
        type: Schema.slack.types.user_id,
        description: "The user that created the announcement draft",
      },
      message: {
        type: Schema.types.string,
        description: "The text content of the announcement",
      },
      channel: {
        type: Schema.slack.types.channel_id,
        description: "The channel where the announcement will be drafted",
      },
      channels: {
        type: Schema.types.array,
        items: {
          type: Schema.slack.types.channel_id,
        },
        description: "The channels where the announcement will be posted",
      },
      icon: {
        type: Schema.types.string,
        description: "Optional custom bot icon to use display in announcements",
      },
      username: {
        type: Schema.types.string,
        description: "Optional custom bot emoji avatar to use in announcements",
      },
    },
    required: [
      "created_by",
      "message",
      "channel",
      "channels",
    ],
  },
  output_parameters: {
    properties: {
      draft_id: {
        type: Schema.types.string,
        description: "Datastore identifier for the draft",
      },
      message: {
        type: Schema.types.string,
        description: "The content of the announcement",
      },
      message_ts: {
        type: Schema.types.string,
        description: "The timestamp of the draft message in the Slack channel",
      },
    },
    required: ["draft_id", "message", "message_ts"],
  },
});

Next we have the handler function for CreateDraftFunction. With it, we create a new datastore record using the put method:

// /functions/create_draft/handler.ts
import { SlackFunction } from "deno-slack-sdk/mod.ts";

import { CreateDraftFunctionDefinition } from "./definition.ts";
import { buildDraftBlocks } from "./blocks.ts";
import {
  confirmAnnouncementForSend,
  openDraftEditView,
  prepareSendAnnouncement,
  saveDraftEditSubmission,
} from "./interactivity_handler.ts";
import { ChatPostMessageParams, DraftStatus } from "./types.ts";

import DraftDatastore from "../../datastores/drafts.ts";

/**
 * This is the handling code for the CreateDraftFunction. It will:
 * 1. Create a new datastore record with the draft
 * 2. Build a Block Kit message with the draft and send it to input channel
 * 3. Update the draft record with the successful sent drafts timestamp
 * 4. Pause function completion until user interaction
 */
export default SlackFunction(
  CreateDraftFunctionDefinition,
  async ({ inputs, client }) => {
    const draftId = crypto.randomUUID();

    // 1. Create a new datastore record with the draft
    const putResp = await client.apps.datastore.put<
      typeof DraftDatastore.definition
    >({
      datastore: DraftDatastore.name,
      item: {
        id: draftId,
        created_by: inputs.created_by,
        message: inputs.message,
        channels: inputs.channels,
        channel: inputs.channel,
        icon: inputs.icon,
        username: inputs.username,
        status: DraftStatus.Draft,
      },
    });

    if (!putResp.ok) {
      const draftSaveErrorMsg =
        `Error saving draft announcement. Contact the app maintainers with the following information - (Error detail: ${putResp.error})`;
      console.log(draftSaveErrorMsg);

      return { error: draftSaveErrorMsg };
    }
    ...

If the call was successful, the payload's ok property will be true, and the item or items property will return a copy of the data you just inserted:

{
  "ok": true,
  "datastore": "drafts",
  "item": {
    "id": "906dba92-44f5-4680-ada9-065149e4e930",
    "created_by": "U045A5X302V",
    "message": "This is a test message",
    "channels": ["C039ARY976C"],
    "channel": "C038M39A2TV",
    "icon": "",
    "username": "Slackbot",
    "status": "draft",
  }
}

If the call was not successful, the payload's ok property will be false, and you will have a error code and message property available:

{
  "ok": false,
  "error": "datastore_error",
  "errors": [
    {
      "code": "some_error_code",
      "message": "A description of the error",
      "pointer": "/datastore/drafts"
    }
  ]
}

Datastore bulk API methods may partially fail.
The partial_failure error message indicates that some items were successfully processed while others need to be retried. This is likely due to rate limits. Call the method again with only those failed items.
You'll find a failed_items array within the API response. The array contains all the items that failed, in the same format they were passed in. Copy the failed_items array and use it in your request.

If you're adding new data via the put or bulkPut method, provide each item with a new primary key value in the id property. If you're updating an existing items, provide the id of each item you wish to replace. Note that a put or bulkPut request replaces each entire specified object, if it exists.

"This datastore size is just right"
The total allowable size of an item (all fields in a record) must be less than 400 KB.

Create or update an item with update

Updating only some of an item's attributes is done with the apps.datastore.update API method. Let's see how that works by passing in values for only some of the datastore's attributes:

// /functions/create_draft_interactivity_handler.ts
...
export const saveDraftEditSubmission: ViewSubmissionHandler<
  typeof CreateDraftFunction.definition
> = async (
  { inputs, view, client },
) => {
  // Get the datastore draft ID from the modal's private metadata
  const { id, thread_ts } = JSON.parse(view.private_metadata || "");

  const message = view.state.values.message_block.message_input.value;

  // Update the saved message
  const updateResp = await client.apps.datastore.update({
    datastore: DraftDatastore.name,
    item: {
      id: id,
      message: message, // This call will update only the message of the draft announcement
    },
  });

  if (!updateResp.ok) {
    const updateDraftMessageErrorMsg =
      `Error updating draft ${id} message. Contact the app maintainers with the following - (Error detail: ${putResp.error})`;
    console.log(updateDraftMessageErrorMsg);
    return;
  }
  ...

If the call was successful, the payload's ok property will be true, and the item property will return a copy of the updated data:

{
  "ok": true,
  "datastore": "drafts",
  "item": {
    "id": "906dba92-44f5-4680-ada9-065149e4e930",
    "created_by": "U045A5X302V",
    "message": "This is a message that will be sent",
    "channels": ["C039ARY976C"],
    "channel": "C038M39A2TV",
    "icon": "",
    "username": "Slackbot",
    "status": "draft",
  }
}

If the call was not successful, the payload's ok property will be false, and you will have a error code and message property available:

{
  "ok": false,
  "error": "datastore_error",
  "errors": [
    {
      "code": "some_error_code",
      "message": "A description of the error",
      "pointer": "/datastore/drafts"
    }
  ]
}

If an item with the provided id doesn't exist in the datastore, update will insert the item using the provided attributes.