Search⌘KHide sidebarOpen menu
Switch to dark mode
Copy page content as Markdown⌘⌥C
Login
Build integrations

Local development

Installing the SDK, running a sync on your machine with the beetl-connect CLI, and packing.

You can build and run a Connect package entirely on your machine. The beetl-connect CLI that ships with the SDK compiles your integration, asks for its settings and credentials, runs a sync against the real API, and writes the records to a local file. Nothing touches your Beetl tenant until you upload the package.

You need Node.js 24.2 or newer and npm.

Set up a project

A package is an npm project with an integration.ts at its root. There is no scaffolding command, so create the files yourself. In a new directory, add package.json:

package.json
{
  "name": "my-integration",
  "version": "0.1.0",
  "private": true,
  "type": "module",
  "files": ["integration.ts"]
}

"type": "module" and the files allowlist are required. Every file the package needs at run time, including an icon or extra .ts modules, must be listed in files.

Install the SDK. It is open source under Apache-2.0, and the source lives at github.com/beetlio/connect.

npm install --save-dev @beetlio/connect

This also writes package-lock.json. Keep it: packing requires it, and Beetl installs exactly the versions it locks.

Then create integration.ts. The smallest working integration reads one record from a public API:

integration.ts
import { defineIntegration, z } from "@beetlio/connect";

const User = z.object({ id: z.number(), login: z.string() });

export default defineIntegration({
  key: "github",
  displayName: "GitHub",
  connection: { origin: "https://api.github.com" },
  syncs: (sync) => ({
    users: sync({
      mode: "replace",
      records: User,
      async *run(ctx) {
        const user = await ctx.json("/users/octocat", User);
        yield { records: [user] };
      },
    }),
  }),
});

Authoring a sync builds a fuller example with credentials, settings and incremental reads.

The CLI writes state and output files into the project directory. Keep them out of version control:

.gitignore
node_modules/
.beetl/
*.ndjson

Configure a sync

configure collects the connection's settings, the sync's settings and the credentials, then saves them as a local profile:

npx beetl-connect configure . users

The first argument is the package directory and the second is the sync key. You can leave out the sync key when the package has only one sync.

The CLI prompts for each field the manifest declares. Secret fields are masked as you type, and credential prompts need an interactive terminal. If the connection defines verify, configure runs it before saving, so a wrong key or URL fails here rather than halfway through a sync. For OAuth, the CLI prints an authorization URL to open in your browser.

To skip the settings prompts, pass the values as one JSON object with a connection and a sync part. This example uses the tickets sync from Authoring a sync:

npx beetl-connect configure . tickets --profile demo \
  --inputs '{"connection":{"baseUrl":"https://acme.example"},"sync":{"pageSize":50}}'
OptionWhat it does
--profile NAMESaves the sync's settings under a named profile. The default is default.
--connection NAMESaves the connection under a name, so several profiles can share it. The default is default.
--origin URLSends requests to a different base URL, such as a local mock server.
--inputs JSONSupplies connection and sync settings without prompting.
--reauthorizeAsks for credentials again, or repeats the OAuth authorization.

Profiles and connections are stored in your operating system's user configuration directory. Credentials in those files are readable only by your user account, but they are not encrypted.

Run a sync

npx beetl-connect sync . users --output users.ndjson

sync uses the saved profile, or runs configure first if there is none. It compiles the package, runs the sync, and prints a summary such as Emitted 5 records in 4 batches. Messages your code writes with ctx.log go to stderr as JSON lines. Press Ctrl+C to stop a run.

OptionWhat it does
--profile NAMERuns with a named profile.
--output PATHWhere to write records. By default the CLI writes a timestamped .ndjson file in the current directory.
--state PATHWhere to keep the checkpoint.

What the output file contains depends on the sync's write mode. Append and Replace syncs write one record per line, and a Replace sync only writes the file once the run succeeds. A Merge sync writes one line per batch, with that batch's records and any deletedKeys.

Checkpoints between runs

If the sync declares a checkpoint, the CLI saves the latest one under .beetl/state/ and passes it to the next run, so a second sync reads incrementally. The default location is tied to the compiled package and the profile, so after you edit the code or change settings the next run starts without a checkpoint. To keep one checkpoint across code changes, pass the same --state path each time. To start over, delete the state file.

Test against a mock API

To exercise pagination, errors or empty pages without a real account, run a small HTTP server on your machine and point a separate connection at it:

npx beetl-connect configure . tickets --profile mock --connection mock \
  --origin http://127.0.0.1:4010
npx beetl-connect sync . tickets --profile mock --output mock.ndjson

Using its own --connection keeps the mock's base URL and credentials apart from the connection you use against the real API.

Authenticated connections require HTTPS, with one exception for loopback addresses such as 127.0.0.1 and localhost, so a plain HTTP mock server works.

The CLI runs your code directly on your machine with your network access. Run only integration code you trust. In Beetl, the same code runs with narrower access; see The sandbox.

Check the package before you pack

configure and sync build the package the way Beetl does: they compile integration.ts with strict TypeScript settings, load it, and validate the definition and the manifest derived from it. Type errors, an invalid key, a settings object that is not a z.strictObject(), or a Merge sync without a primary key all fail at this step, with an error that names the problem.

There is no separate validate command, and pack neither compiles nor runs your code. Run a sync after your last change and before you pack, so a mistake shows up on your machine instead of as a failed build in Beetl.

Pack

npx beetl-connect pack . --output my-integration.tgz

pack prints every file it included. Read that list before you hand the file to a TenantAdmin. The rules a package must meet, and the upload steps, are on Pack and upload.

Every command has --help, for example npx beetl-connect sync --help.

Last updated on

On this page