Typescript Integrations
TypeScript is a first-class language for building integrations with the CLI.
Support for TypeScript integrations was significantly expanded and improved with the release of v17 of the Zapier CLI and platform. This document covers how to create, use, and test TypeScript integrations.
Getting Started
The TypeScript+ESM template generated with zapier init
provides the
configuration needed to get started with TypeScript integrations. If you
are adding TypeScript support to an existing app, you can check the
Structure of a TS Integration section
below for more details information about compiler settings, using the
zapier-platform-core
library, and how to compose Triggers, Creates,
and Searches together with TypeScript.
This will create a new app in ./my-app
with the following structure,
and install the dependencies.
Differences from JS Integrations
There are a few important differences between how TypeScript and JavaScript integrations are implemented that are worth noting.
- The
define
helper functions are important to wrap your App, Triggers, Creates, Searches, and Input Field definitions. - Code is kept in the
src/
directory and compiled todist/
. The root of the integration now becomes./src/index.ts
. - Modern
import
/export
syntax is used instead of Node’srequire
/module.exports
assignments. - We recommend testing with Vitest, a drop-in replacement for Jest that is faster and has better ESM+TypeScript support.
Input Fields
The defineInputFields
helper function should be used to define the
inputs for all of Triggers, Creates, and Searches. This helper will
automatically infer the types of all of the input fields specified.
This looks like:
This is a departure from the previous approach of typically defining the inputs and sometimes perform functions inside of the top-level exported action.
Inferring Input Data from Input Fields
The InferInputData
type can derive the shape of the input data from
input fields defined with defineInputFields
. This is useful because it
provides the correct types for the bundle.inputData
property to give
to the various perform
functions.
Reusing Input Fields
Input Fields are frequently reused across triggers, creates, and
searches. To make this easier, there is also a singular
defineInputField
helper that can be used to define an input field.
These can be put somewhere in the src/
directory, and then imported by
the actions that need them.
Dynamic Input Fields
Inputs fields can be dynamic, meaning they are functions that get executed and can return zero, one, or more input fields determined at runtime. This is useful when the input fields are dependent on the values of other input fields, or input field definitions are fetched and prepared from your API, like is the case for CRM and Database APIs.
The example below shows a dynamic input field that is used to optionally
include a custom subject field when a prior boolean input field is set
to true
.
Inside of dynamic input field functions, bundle.inputData
will not have type
information for its sibling input fields. We recommend casting referenced
fields to their known types. For example above, note
inputData.useCustomSubject as boolean
.
Known vs Unknown Fields
The example above shows a known dynamic input field, where the key and
type are known ahead of time, and the function’s logic is used to
include it or not, based on other fields. These sorts of input fields
can be captured by the type system and included in the
bundle.inputData
property. Input fields returned from input functions
are always considered optional, even if they have required: true
in
their definition, as they cannot be guaranteed to be present.
When fields cannot be known ahead of time, the input function can return
completely unknown input fields. This is useful when the input fields
are derived from data returned from an API. In this case, known inputs
are preserved, but the bundle.inputData
property will consider any
other properties as unknown
.
Perform Function Types
There are now dedicated types for all of the different types of Perform
Action. These should be imported with type
qualified imports. The
relevant operation
sections inside the define
helpers will inform
and enforce the correct type for the different perform functions. They
are:
- Polling Triggers:
PollingTriggerPerform
- Webhook Triggers:
WebhookTriggerPerform
WebhookTriggerPerformList
WebhookTriggerPerformSubscribe
WebhookTriggerPerformUnsubscribe
- Creates:
CreatePerform
CreatePerformResume
- Searches:
SearchPerform
SearchPerformResume
They all take at least one type parameter, which is the shape of the the
bundle’s inputData
property. These Perform types should use
satisfies XyzPerform<…>
to enforce the correct types but preserve the
return type information.
In most cases though, input data is derived from the input fields, so
InferInputData
type can be
used:
The bundle.inputData
property is now typed as the inferred input data
from the inputFields
array.
Structure of a TS Integration
TypeScript integrations follow the same structure and underlying
schema as any
JavaScript integration. The important difference is that TypeScript apps
require important components of integrations to be wrapped with the
relevant define
helper functions to provide deep type inference about
the application. These are:
defineApp()
– Main function for the top-level app.defineTrigger()
/defineCreate()
/defineSearch()
– For the relevant actions in an integration.defineInputFields()
– Wraps an array of input field definitions to simplify handling full typing information about the input fields.
src/index.ts
The main entry point of the app becomes src/index.ts
. It should import
its dependencies, and the default export remains as the Application
object. Wrapping it with defineApp
will help to check its structure.
Otherwise it’s a normal integration, and you register Auth, middleware, hydrators, Triggers, Creates, and Searches all the same way!
src/authentication.ts
Authentication is defined as a normal object and exported from a file
named src/authentication.ts
. It uses a satisfies Authentication
constraint to check its structure, and does not use a define
helper.
src/middleware.ts
Middleware functions are functions exported from src/middleware.ts
,
that are typed as BeforeRequestMiddleware
or
AfterResponseMiddleware
types. They are registered in the same way as
in JavaScript integrations in /src/index.ts
.
src/triggers/pollingTrigger.ts
Triggers, Creates, and Searches are now recommended to define their
inputs and perform functions as separate objects, and composed together
in the define
helper that is default export from the file.
src/tsconfig.json
The tsconfig.json
file configures the TypeScript compiler. Below are
settings that are recommended for developing TypeScript integrations.