We recently covered API connectors from a high-level perspective, what they are and what they do. We noted that they are used with both enterprise iPaaS and embedded iPaaS to expedite the creation of integrations. I want to look at one of the API connectors we built for the Prismatic embedded integration platform – a peek behind the scenes to show you what we've done and give you some ideas for building your own custom connectors.
Our example connector (we also call them components) is one we created to connect with Asana, a project/task management system. I've made the code for this connector available via Prismatic's public GitHub repository.
I wrote the Asana API connector in TypeScript, which then compiles to JavaScript. The API connector talks to Asana through its REST API over HTTP with JSON as the transport language for both requests and responses. The REST API also supports form-data (file) requests when you need to deal with binary files (PDFs, images, etc). In addition to the REST endpoints, Asana provides webhook support for sending event-driven updates to an integration.
API connector code file structure
While you could conceivably place all the code for a very simple connector in a single .ts
file, this isn't a simple connector. And best practices require that we organize the code somewhat logically. So, from the top level of the src/
directory, here's what we have:
actions/
directory: all the actions/functions that the connector supportsclient.ts
: an HTTP client to connect to Asana API. I like using the NPM package axios.connections.ts
: we support both API key and OAuth 2.0index.ts
: imports all other functions in the connector and the Prismatic component code and exports the Asana componentinputs.ts
: a complete list of the data (field) inputs used with the actions. Inputs could be specified in-line when you define an action, but keeping them all together makes them reusable between actions.triggers.ts
: webhooks and HMAC validationutil.ts
: various options used throughout the connector
Further, within the actions
directory, actions are grouped by resource type (attachment, customFields, items, portfolio, etc.) with each action's name reflecting its function.
What does the Asana API connector do?
In our overview post on API connectors, we noted that connectors include functionality to work with or define webhook behavior, auth, and actions for the corresponding integration. Let's look at each of these for the Asana API connector. In addition, we'll also examine the the built-in Axios HTTP client.
Perform actions
The actions are the bulk of the connector. Currently, the API connector includes 60+ actions, primarily performing CRUD (create, read, update, and delete) functions against Asana resources from projects and tasks to teams and users. Each action is named for the function it serves. So, if we want to retrieve a task from the API, we would call getTasks
, but if we need to retrieve a list of the tasks, we would call listTasks
instead. These actions wrap Asana's get a task (GET /tasks/{task_gid}
) and get multiple tasks (GET /tasks
) API endpoints respectively.
Handle webhooks
If we want Asana to push data to us (rather than fetching the data), Asana also supports webhooks. The actions in actions/webhooks.ts
allow an integration to configure webhooks in Asana, and the trigger defined in triggers.ts
implements HMAC verification so we can be sure that webhook requests we receive are genuine and come from Asana.
Handle auth
Auth is handled via connections.ts
. As noted, the API supports auth via either API key or OAuth 2.0. You can use an API key to access the API for testing purposes, but if you want to give your customers the best deployment experience by giving them a single button to click to log in, I would strongly recommend OAuth 2.0.
Run HTTP client
Since we are accessing a REST API, we need some means of sending requests via HTTP. Axios is a common promise-based NodeJS HTTP client library that contains some handy response deserialization and error handling. We create a reusable Axios client in client.ts
.
Action(s)!
As noted above, the Asana connector has over 60 actions. I'm not going to go through all of them here (you can do that via GitHub), but I would like to examine the code for a handful of them so you can get an idea of the scope of actions provided by the connector.
We'll look at the following actions in detail:
getProject
updatePortfolio
addUserToTeam
deleteTag
attachFileToTask
getProject
This action allows you to retrieve detailed data for an Asana project record. It imports the HTTP client using client.ts
and the list of possible inputs from inputs.ts
.
The action then creates the HTTP client and uses the GET method to send projectId
as a parameter to the API, along with expected field inputs.
The last half of the code for this action is a sample payload, making it very clear to devs who may be troubleshooting an issue what to expect when we run this code.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
updatePortfolio
This action allows for a specific Asana portfolio record to be updated (partially changed). It imports the HTTP client using client.ts
and the list of possible inputs from inputs.ts
.
updatePortfolio
action then creates the HTTP client and uses the PUT method to send portfolioId
as a parameter to the API, along with expected field inputs.
This code does not define an example payload inline, but does import it from portfolioPayload
since the payload data is extensive. It would be easy to lose focus with a wall of example payload text.
123456789101112131415161718192021222324252627282930313233343536373839
addUserToTeam
This action adds a user to a specific Asana team. It imports the HTTP client using client.ts
and the list of possible inputs from inputs.ts
. It then uses the POST method to send TeamId
as a parameter to the API, along with expected field inputs.
This action includes an example payload showing the team and user data.
123456789101112131415161718192021222324252627282930313233343536373839
deleteTag
This action removes an Asana tag. It imports the HTTP client using client.ts
and the list of possible inputs from inputs.ts
.
deleteTag
then creates the HTTP client and uses the DELETE method to send the tagId
as a parameter to the API, along with expected field inputs.
The example payload in this case is empty.
1234567891011121314151617
attachFileToTask
The actions we've looked at before this one were all focused on creating, reading, updating and deleting records. This one introduced file handling into the mix. This action attaches a file to an Asana task. To do so, it imports form-data
(for handling the file), imports the HTTP client using client.ts
, and pulls in the list of possible inputs from inputs.ts
.
attachFileToTask
then creates the HTTP client and the formData
object and uses the POST method to send fileName
and taskId
as parameters to the API, along with expected field inputs. form-data
handles the creation of the multi-part upload, and necessary related headers.
The example payload for this is perhaps the simplest of the actions we've looked at, as it includes just a handful of fields for describing the file attachment.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
Best practices for API connectors
Most of the Prismatic iPaaS users I work with use a solid mix of built-in connectors and custom API connectors that allow them to connect with the verticals and niche applications their customers need. I trust that this walkthrough for one of our API connectors has provided some patterns that will be useful as you build your own custom connectors.
That said, here are a few things to keep in mind:
- Wrap everything related to connecting to the API (auth URL, API version, authentication fields, and more) in a connection function.
- Build actions to be reusable across integrations.
- Remember that you don't need a one-to-one correlation between actions and endpoints.
- Use clear examples and comments for input fields and sample payloads.
If you'd like to see more content on API connectors and other integration topics, follow us on LinkedIn or Twitter.