Skip to main content
A data source is a function that fetches data from an external API and returns it in a format that Prismatic renders inside a config wizard. Data sources are defined with dataSource() and collected in the dataSources field of component().

DataSourceDefinition type

interface DataSourceDefinition<
  TInputs extends Inputs,
  TConfigVars extends ConfigVarResultCollection,
  TDataSourceType extends DataSourceType,
> {
  /** How this data source appears in the Prismatic UI. */
  display: ActionDisplayDefinition;
  /** Function that fetches and returns data. */
  perform: DataSourcePerformFunction<TInputs, TConfigVars, TDataSourceType>;
  /** The UI control type to render the result. */
  dataSourceType: TDataSourceType;
  /** Input fields presented to integration builders. */
  inputs: TInputs;
  /** Example output shown in the UI. Must match TDataSourceType. */
  examplePayload?: Awaited<ReturnType<this["perform"]>>;
  /**
   * Name of another data source in this component that provides
   * supplemental detail (e.g., example field values) for this data source.
   */
  detailDataSource?: string;
}

DataSourceType values

The dataSourceType field controls which UI element renders the data returned by perform:
dataSourceTypeReturn typeUI rendered
"picklist"string[] or Element[]Dropdown list
"string"stringText input pre-filled with the value
"date"stringDate picker pre-filled with the value
"timestamp"stringDate/time picker pre-filled with the value
"boolean"booleanToggle pre-set to the value
"number"numberNumeric input pre-filled with the value
"code"stringCode editor pre-filled with the value
"objectSelection"ObjectSelectionObject and field selector
"objectFieldMap"ObjectFieldMapField mapping UI
"jsonForm"JSONFormDynamic JSON Forms UI
"schedule"{ value: string }Schedule picker

DataSourceResult

All perform functions must return a DataSourceResult matching the declared dataSourceType:
type DataSourceResult<TDataSourceType extends DataSourceType> = {
  /** The data returned; type depends on dataSourceType. */
  result: DataSourceTypeMap[TDataSourceType];
  /**
   * Optional extra data for out-of-band processing.
   * Only available when called from a config wizard page.
   */
  supplementalData?: { data: unknown; contentType: string };
};

Picklist example

The most common data source type. Returns a list of strings (or labeled elements) that populate a dropdown:
import { dataSource, input } from "@prismatic-io/spectral";

export const listProjects = dataSource({
  display: {
    label: "List Projects",
    description: "Fetch all projects the connected account can access",
  },
  dataSourceType: "picklist",
  inputs: {
    connection: input({
      label: "Connection",
      type: "connection",
      required: true,
    }),
  },
  perform: async (context, params) => {
    const apiKey = params.connection.fields.apiKey as string;

    const response = await fetch("https://api.example.com/projects", {
      headers: { Authorization: `Bearer ${apiKey}` },
    });

    if (!response.ok) {
      throw new Error(`Failed to fetch projects: ${response.status}`);
    }

    const projects = (await response.json()) as { id: string; name: string }[];

    // Return labeled elements so the UI shows the name but stores the ID
    return {
      result: projects.map((p) => ({ key: p.id, label: p.name })),
    };
  },
  examplePayload: {
    result: [
      { key: "proj_01", label: "Marketing Automation" },
      { key: "proj_02", label: "Data Sync" },
    ],
  },
});

ObjectSelection example

objectSelection lets integration builders choose which objects (and optionally which fields on those objects) to include:
import { dataSource, input } from "@prismatic-io/spectral";

export const selectCrmObjects = dataSource({
  display: {
    label: "Select CRM Objects",
    description: "Choose which CRM objects to sync and which fields to include",
  },
  dataSourceType: "objectSelection",
  inputs: {
    connection: input({ label: "Connection", type: "connection", required: true }),
  },
  perform: async (context, params) => {
    const apiKey = params.connection.fields.apiKey as string;

    const response = await fetch("https://api.example.com/schema", {
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    const schema = await response.json();

    return {
      result: schema.objects.map((obj: { name: string; fields: string[] }) => ({
        object: { key: obj.name, label: obj.name },
        fields: obj.fields.map((f) => ({ key: f, label: f })),
        defaultSelected: true,
      })),
    };
  },
});

ObjectFieldMap example

objectFieldMap lets integration builders map fields from one system to fields in another:
export const mapContactFields = dataSource({
  display: {
    label: "Map Contact Fields",
    description: "Map source system fields to CRM contact fields",
  },
  dataSourceType: "objectFieldMap",
  inputs: {
    connection: input({ label: "Connection", type: "connection", required: true }),
  },
  perform: async (context, params) => {
    return {
      result: {
        fields: [
          {
            field: { key: "firstName", label: "First Name" },
            mappedObject: { key: "contacts", label: "Contacts" },
            mappedField: { key: "first_name", label: "First Name" },
          },
          {
            field: { key: "email", label: "Email Address" },
            mappedObject: { key: "contacts", label: "Contacts" },
            mappedField: { key: "email", label: "Email" },
          },
        ],
        options: [
          {
            object: { key: "contacts", label: "Contacts" },
            fields: [
              { key: "first_name", label: "First Name" },
              { key: "last_name", label: "Last Name" },
              { key: "email", label: "Email" },
            ],
          },
        ],
      },
    };
  },
});

JSONForm example

jsonForm renders a dynamic form driven by a JSON Schema and UI schema (using the JSON Forms library):
export const configureNotifications = dataSource({
  display: {
    label: "Configure Notifications",
    description: "Set notification preferences using a dynamic form",
  },
  dataSourceType: "jsonForm",
  inputs: {},
  perform: async (context, params) => {
    return {
      result: {
        schema: {
          type: "object",
          properties: {
            email: { type: "string", format: "email", title: "Email Address" },
            frequency: {
              type: "string",
              enum: ["daily", "weekly", "monthly"],
              title: "Frequency",
            },
            enabled: { type: "boolean", title: "Notifications Enabled" },
          },
          required: ["email", "frequency"],
        },
        uiSchema: {
          type: "VerticalLayout",
          elements: [
            { type: "Control", scope: "#/properties/email" },
            { type: "Control", scope: "#/properties/frequency" },
            { type: "Control", scope: "#/properties/enabled" },
          ],
        },
        data: { frequency: "weekly", enabled: true },
      },
    };
  },
});

Using detailDataSource

Set detailDataSource to the key of another data source in the same component. When the integration builder selects items using this data source, the detail data source is called to provide richer metadata (such as available field values for the selected objects):
export const listObjects = dataSource({
  display: { label: "List Objects", description: "" },
  dataSourceType: "objectSelection",
  inputs: { connection: input({ label: "Connection", type: "connection", required: true }) },
  detailDataSource: "objectDetail",
  perform: async (context, params) => { /* ... */ },
});

export const objectDetail = dataSource({
  display: { label: "Object Detail", description: "Supplemental field details" },
  dataSourceType: "objectFieldMap",
  inputs: { connection: input({ label: "Connection", type: "connection", required: true }) },
  perform: async (context, params) => { /* ... */ },
});
Use Element[] instead of string[] for picklist results when you want the UI to display a human-readable label but store a programmatic key (such as an ID).