← Build log · Part 3 of 8

Properties, pipelines and forms as code

Idempotent scripts that create things the first time and update them every time after.

HubSpotAutomation

A setup script is only useful if you can run it twice. Each script here reads a JSON file from config/ and makes the portal match it.

The upsert pattern

For properties the API returns 409 when one already exists, so the script catches that and sends a PATCH instead. Running the whole thing again changes nothing unless the JSON changed. Every script also takes --dry-run.

try { await api('POST', url, prop) }
catch (e) { if (e.status === 409) await api('PATCH', url + '/' + prop.name, prop) }

Ticket pipeline

The default pipeline's stages are renamed by position to New, In progress, Waiting on customer and Closed. The script never deletes a stage, because deleting one that tickets sit in is a mistake you only make once.

Forms have opinions

The Forms v3 API rejected our first attempts one error at a time. What we learned: a field group holds at most three fields, richTextType must be lowercase, a validation block is required, and timestamps such as createdAt must not be sent back on update. Hidden fields are set with a defaultValue, which turned out to be the key to the next post.

Kulkarni Inc.
Notes from building the Kulkarni Inc. stack. See all