Writing Custom Components in Prismatic

Writing Custom Components in Prismatic

This post refers to an earlier version of Prismatic. Consult our docs on writing custom components or contact us if you'd like help with any of the topics addressed in this post.

Prismatic offers an ever-growing number of components out of the box – see our component catalog for a full list. These built-in components handle common integration tasks like interacting with HTTP-based APIs, reading and modifying files in Azure, S3 and Dropbox, sending messages with SendGrid, Slack, and Twilio, transforming data from one format to another, and more. Large portions of your integrations can be assembled using these pre-built components.

These general-use components don't cover every use case, though. Eventually you find yourself needing to write some business logic that can't be handled by a built-in component, either because it's specific to your industry or product, or because of a third-party vendor's, ahem, "non-standard" implementation. When that happens, you will need to write a reusable custom component or short code component step for your integration. Let's dive in to when to use code components and custom components, and then walk through writing a simple custom component from start to finish.

Code components vs. custom components

Let's look at the two methods Prismatic provides for incorporating custom code into your integrations:

The code component allows you to write short JavaScript code snippets. It's used to write simple one-off functions that are specific to an integration, and it's very quick and easy to add. If you can't solve a problem with built-in components, but can solve it with a few lines of JavaScript, you should add a code component to your integration.

Here's a code component I wrote recently that converts data from one unit of measurement to another:

Screenshot of a code component

If your custom business logic is more complex, or if you want to reuse your code in multiple integrations, you'll want to write a custom component instead. Another reason to use a custom component is that just like built-in components, custom components can have multiple "actions". For example, Prismatic's built-in Azure Files component contains actions for uploading files, deleting files, listing files, etc. You, similarly, can combine multiple related actions into a single custom component. Suppose, for example, that you need to build multiple integrations that involve converting units from one measurement to another. You could create a reusable custom component that does unit conversions (and we will in a moment!).

Now, which one – code component or custom component – should you choose when you want to write some custom code? To make that decision, ask yourself a few questions:

  1. Would it be handy to be able to reuse the code in other integrations in the future?
  2. Is your code complex enough that it would benefit from unit testing?
  3. Does your code require non-standard libraries that aren't bundled up with the typical Node.js distribution?
  4. Do you want to bundle multiple related actions together into a single cohesive package?

If you answer "yes" to any of those questions, you should write a custom component.

We'll look at the code component in a future blog post. Today, let's check out what it takes to write a custom component from scratch.

Writing a custom component from scratch

Code for the component referenced in this example is available on GitHub.

For our example, let's suppose we find ourselves needing to convert rocket fuel between pounds and gallons in several of our integrations. Computing pounds to gallons (and its inverse, gallons to pounds) are related actions, and definitely reusable, so a custom component makes sense for us here. This example is meant to illustrate the process of writing a custom component that has a few related actions. You can inject whatever business logic you want into your actions – Prismatic works with any industry or vertical.

Let's start by creating a new npm project for our component. If you have not done so yet, download and install Prismatic's CLI tool, prism, with:

yarn global add @prismatic-io/prism

Next, create a new custom component project with:

prism components:init fuel-unit-converter

Now, what did that command do? You'll notice that it created a new directory called fuel-unit-converter that contains a few files:

fuel-unit-converter
├── assets
│   └── icon.png
├── package.json
├── src
│   └── index.ts
├── tsconfig.json
├── webpack.config.js
└── yarn.lock

In addition initializing a standard Node.js project with TypeScript and Webpack support, we created a couple of directories (assets and src) that contain an icon for our component and our source code respectively. You can change assets/icon.png if you would like. If you inspect package.json, you'll see that it installed some dev dependencies (TypeScript and Webpack) and a dependency to Prismatic's custom component library, Spectral.

{
  "name": "fuel-unit-converter",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "private": true,
  "devDependencies": {
    "copy-webpack-plugin": "^6.2.1",
    "ts-loader": "^8.0.6",
    "typescript": "^4.0.3",
    "webpack": "^5.1.3",
    "webpack-cli": "^4.1.0"
  },
  "dependencies": {
    "@prismatic-io/spectral": "^1.0.5"
  }
}

Now that we have an environment set up, we're ready to write some code. Fire up your favorite editor and open src/index.ts . Today we'll write our whole custom component in a single file, though for readability's sake you can separate inputs, utilities, etc., as you wish. For an example of separating out your code into multiple files, check out the progix-sig-check custom component from our Examples repo.

At the top of index.ts let's start by importing a few functions and values:

import { action, component, input } from "@prismatic-io/spectral";
import { version } from "../package.json";

The first line imports TypeScript function definitions from Prismatic's Spectral library. This allows us to write our own component that takes one or more input values and performs an action using that input. The second line just pulls in the version from package.json so you can version your custom component in one place.

Next, we'll define a couple of inputs for our actions. Both actions, gallons to pounds and pounds to gallons, will take a fuel type and amount of fuel as inputs, so we can use the same input fields for both actions. The input key is important, as we'll reference it in our action later, and the label determines how the inputs will look in the web app UI – we'll see those show up in screenshots in a moment:

const fuelTypeInputField = input({
  key: "fuelType",
  label: "Fuel Type",
  type: "string",
  placeholder: "Type of Fuel",
  example: "Hydrazine",
});

const fuelAmountInputField = input({
  key: "fuelAmount",
  label: "Fuel Amount",
  type: "string",
  placeholder: "Fuel amount to be converted in gallons or pounds",
  example: "100",
});

Next up, we'll include a unit conversion table that both actions will use:

const gallonsToPoundsConversion = {
  Hydrazine: 8.38,
  Kerosene: 6.68,
  Nitromethane: 9.49,
  O2: 9.52,
};

Now it's time for us to create an action. An action is made up of a unique key, some information on how the web app should display the action, what inputs the action takes in (fuel type and amount in this case), and a perform function that executes when the action is run.

Our gallons to pounds action's perform function is fairly straight forward: two parameters, fuelType and fuelAmount (in gallons) are passed in. An error is thrown if fuelType doesn't match a known rocket fuel type in our conversion table. The function returns an amount of fuel, converted to pounds:

const gallonsToPoundsAction = action({
  key: "gallonsToPounds",
  display: {
    label: "Gallons to pounds",
    description: "Convert gallons of fuel to pounds of fuel",
  },
  inputs: [fuelTypeInputField, fuelAmountInputField],
  perform: async (context, { fuelType, fuelAmount }) => {
    if (!(fuelType in gallonsToPoundsConversion)) {
      throw Error(`${fuelType} is not a valid fuel type.`);
    }
    return {
      data: fuelAmount * gallonsToPoundsConversion[fuelType],
    };
  },
});

Our pounds to gallons action is almost identical, with the exception that it takes pounds as input, and returns gallons by dividing instead of multiplying:

const poundsToGallonsAction = action({
  key: "poundsToGallons",
  display: {
    label: "Pounds to gallons",
    description: "Convert pounds of fuel to gallons of fuel",
  },
  inputs: [fuelTypeInputField, fuelAmountInputField],
  perform: async (context, { fuelType, fuelAmount }) => {
    if (!(fuelType in gallonsToPoundsConversion)) {
      throw Error(`${fuelType} is not a valid fuel type.`);
    }
    return {
      data: fuelAmount / gallonsToPoundsConversion[fuelType],
    };
  },
});

We now have inputs and actions. The final thing we need to do is export our component. Like an action, a component is made up of a unique key and some information about how the UI should display the component, as well as the component's version, whether the component should be public or private, and what actions are included in the component:

export default component({
  key: "fuel-unit-converter",
  public: false,
  version,
  display: {
    label: "Convert units",
    description: "Convert units of fuel between gallons and pounds",
    iconPath: "icon.png",
  },
  actions: { ...gallonsToPoundsAction, ...poundsToGallonsAction },
});

That's it! Our component is written up. Next, let's package up the component, publish it, and make sure it works properly. From the base fuel-unit-converter/ directory run:

yarn webpack
prism components:publish --no-confirm

You'll see our new component show up on the components page within the Prismatic web app:

Screenshot of published component

We can add actions from our component to an integration to verify that our code functions as expected:

Screenshot of testing component

Further reading

There we have it – a custom component built from scratch. We went through how to set up a new component project, writing up inputs, actions, helper functions, and the component itself, and how to deploy your code to Prismatic.

To learn more about custom components, check out our Writing Your First Custom Component quickstart to see how Prismatic's Slack component was built. When you're feeling comfortable with the basics, check out our Building an Advanced Component quickstart to learn about handling credentials and binary data. For documentation on properly logging, handling input parameters, outputting data appropriately, etc., see our docs article on Writing Custom Components.

If you have any questions, or just want to chat about integrations, reach out – we'd love to chat!


About Prismatic

Prismatic is the integration platform for B2B software companies. It's the quickest way to build integrations to the other apps your customers use and to add a native integration marketplace to your product. A complete embedded iPaaS solution that empowers your whole organization, Prismatic encompasses an intuitive integration designer, embedded integration marketplace, integration deployment and support, and a purpose-built cloud infrastructure. Prismatic was built in a way developers love and provides the tools to make it perfectly fit the way you build software.

Get the latest from Prismatic

Subscribe to receive updates, product news, blog posts, and more.