Skip to main content

Getting Started with AppDirect Functions

AppDirect Functions is a feature designed to extend the capabilities of the AppDirect platform through custom, serverless code. This guide will help you get started with Functions and will walk you through the steps to create your first Function. No prior knowledge is required.

For more information on core concept and capabilities, refer to Early Access AppDirect Functions

Requirements

There are not many requirements for developing an AppDirect Function. Functions are developed using Node.js 22 and may use any external npm package. The function should be packaged as a zip file containing your code and all the npm packages it needs.

The next sections demonstrate how to create an example Function that handles Company webhook events and synchronizes companies created in the AppDirect platform with HubSpot CRM.

📝 Note: The code snippets in the sections below use JavaScript, but you can also use any language that compiles to JavaScript, such as TypeScript.

Creating your Function

The first step in developing your function is to create a Node.js project. Ensure that the npm tool is installed (see the link above). In the directory where you want your project, run the following command:

npm init

Answer the npm prompts to generate a package.json file in your directory. After that, you can use your preferred IDE to start writing your function code.

The packaged zip file must contain an index.js file at the root with the following signature:

exports.handler = async (event, context) => {

let statusCode = 200;
return {
statusCode
};
};

The exported handler must follow this signature and return an object with a statusCode field indicating the function’s execution result.

The event parameter of the handler function contains the webhook payload in the form of a JSON object, already deserialized. So event provides access to the webhook payload properties. For more information on the different types of webhook payloads, refer to webhook notification payload.

We’ll create a new Javascript file to hold the HubSpot specific logic and API calls. Let’s place this file next to the existing one and name it as hubspot.js. Once created, we’ll define the following function:

exports.syncCompanyFromWebhook = async (companyWebhookPayload) => {
}

And from our existing index.js file, we’ll call this new function and pass the complete webhook payload to it, as follows:

const { syncCompanyFromWebhook } = require('./hubspot');

exports.handler = async (event, context) => {

let statusCode = 200;

async syncCompanyFromWebhook(event);

return {
statusCode
};
};

Notice how we have imported our new HubSpot logic using the familiar require statement at the top of the file. We are then passing the event parameter to the function which will provide access to the webhook payload properties we need.

In order to facilitate the task of calling the HubSpot APIs, we will use the HubSpot CRM SDK, which we can install using the following command:

npm install @hubspot/api-client

📝 Note: At the moment of writing this guide, the @hubspot/api-client package is at version 13.0.0.

Let’s import the HubSpot SDK by adding the necessary require statement to hubspot.js and then create a HubSpot client object, as follows:

const hubspot = require('@hubspot/api-client');

exports.syncCompanyFromWebhook = async (companyWebhookPayload) => {
const hubspotClient = new hubspot.Client({ accessToken });
}

Calling the HubSpot APIs require an access token for authentication purposes. For more information on how to create these, refer to HubSpot's documentation. After creating an access token, you need to make it available to your function. It’s best not to hardcode secret configuration values in your code. This is where environment variables come in. In AppDirect’s configuration panel, you can define environment variables and their values. These variables are securely stored, encrypted at rest, and passed to your function’s execution context at runtime. They can be retrieved by accessing Node’s process.env object, as follows:

const { syncCompanyFromWebhook } = require('./hubspot');

exports.handler = async (event, context) => {

let statusCode = 200;

async syncCompanyFromWebhook(event, process.env.ACCESS_TOKEN);

return {
statusCode
};
};

The code above assumes the HubSpot access token is available via an ACCESS_TOKEN variable (we’ll cover later how to configure this variable). The token is passed as the second parameter to the syncCompanyFromWebhook function. The function now looks like this:

const hubspot = require('@hubspot/api-client');

exports.syncCompanyFromWebhook = async (companyWebhookPayload, accessToken) => {
const hubspotClient = new hubspot.Client({ accessToken });

}

We now have a HubSpot client fully initialized and ready to call HubSpot APIs. The next step is to actually retrieve the webhook payload properties necessary for creating the company within HubSpot. You can pass many properties to HubSpot, but at minimum, you need the company name and website. You also need to distinguish between new companies and existing ones that require updates. To do this, extract the action (ADDED, CHANGED, or REMOVED) that triggered the webhook. Considering these points, the code would start to look like this:

const hubspot = require('@hubspot/api-client');

exports.syncCompanyFromWebhook = async (companyWebhookPayload, accessToken) => {
const hubspotClient = new hubspot.Client({ accessToken });
const action = companyWebhookPayload.resourceAction;
const company = companyWebhookPayload.resource.content.name;
const website = companyWebhookPayload.resource.content.website

switch (action) {
case 'ADDED': {
return await createCompany(hubspotClient, company, website);
}
case 'CHANGED': {
return await updateCompany(hubspotClient, company, website);
}
case 'REMOVED': {
return await deleteCompany(hubspotClient, company);
}
}
}

createCompany = async (hubspotClient, company, website) => {
console.log(`creating company '${company}' with website '${website}'`);

const newCompany = {
properties: {
name: company,
domain: website,
}
};

const response = await hubspotClient.crm.companies.basicApi.create(newCompany);

console.log(`company created in HubSpot, id = ${response.id}`);
return response.id;
}

updateCompany = async (hubspotClient, company, website) => {
console.log('todo => implement updates');
return company;
}

deleteCompany = async (hubspotClient, company) => {
console.log('todo => implement deletion');
return company;
}

Webhook payload properties can be extracted by navigating the object structure. These properties are packaged in the format expected by the HubSpot create API. A new HubSpot company ID is returned to indicate successful creation. Handling updates and deletions is left as an exercise. Log statements in the code appear in the console and can be viewed in AppDirect’s UI for troubleshooting.

The final step is to return a response code that reflects the function’s successful execution. Here’s the completed index.js file:

const { syncCompanyFromWebhook } = require('./hubspot');

console.log(`Received function call on ${new Date()}`);
const newCompanyId = await syncCompanyFromWebhook(event, process.env.ACCESS_TOKEN);

statusCode = newCompanyId != null ? 200 : 500;
return {
statusCode,
body: {
"message": statusCode === 200 ? "all good" : "company could not be created",
}
};

By checking the returned company ID, we determine the proper response code to return from the function. This status code must correspond to an HTTP status code. A body parameter can also be added to the function’s return object. This body will be available in the webhooks details page.

To recap, here’s what you should have in your function directory after going through all of these steps (excluding the content of the node_modules directory):

.
├── hubspot.js
├── index.js
├── node_modules
│ ├── ...
├── package-lock.json
└── package.json

After completing these steps, you can zip the whole directory. You can do this using your preferred tool. If you are on Linux or MacOS, you can run the following command in your function’s directory:

zip -r demo-function.zip .

You should have a zip file of roughly 7.5 MB, as it includes the node_modules directory with the HubSpot SDK and all its dependencies.

Configuring your Function

Next, we’ll look at how to configure your function within the AppDirect portal.

  1. Log into AppDirect and navigate to the Marketplace Settings (Go to Manage > Marketplace > Settings). You will see a Functions item in the left-hand side menu. If you click the Functions link, you will see the Functions list, which should be empty:

  2. Click Create New Function to open the Function creation page:

  3. Enter the name of your function, attach the zip file of your function’s code, and last but not least, define the ACCESS_TOKEN environment variable. This variable should contain your real HubSpot access token value.

  4. Click Save. The AppDirect platform uses this information to start the provisioning process. This may take a few seconds, depending on the size of your function. After provisioning, you’ll be redirected to the Functions list. You may need to refresh the page to see the final status. If successful, a green status pill will appear next to your function:

Creating a webhook with your Function

To make your function useful, it must be linked to a webhook. Next, create a webhook and point it to your function. To do this,

  1. Go to the Webhooks page in Marketplace Settings.

  2. Click the New Webhook button to open the Webhook configuration page. Select the Local Function option and select your newly created function. Enter the remaining fields.

  3. Click Save. You now have a webhook configured to use your function as its handler. To view the execution logs for the function, check the Function's page for the last invocation. This information is helpful to troubleshoot any issues with your Function.

Resource limits

AppDirect enforces limits on function execution to ensure reliability and stability for all customers. The zip file you upload, containing your function and dependencies, must be under 150 MB. Larger files are not accepted. Each function has a maximum of 128 MB of memory and must complete execution within 3 seconds.

Was this page helpful?