Custom Code Overview

> Write Brickr nodes in local TypeScript or JavaScript, test them on your machine, then publish them into your workspace.

Brickr custom code nodes are the code-native extension system for the Builder. Instead of composing a node visually, you define a node in a .br.ts or .br.js file, describe its inputs and outputs with the SDK, and publish it to your workspace with the CLI.

This gives you a clean split between three stages:

| Stage | Where it runs | What it is for | |-------|---------------|----------------| | Local | Your machine | Author node files and run local tests with brickr dev | | Dev | Brickr cloud dev channel | Publish the latest node versions for Builder testing with brickr deploy dev | | Prod | Brickr production runtime | Promote the current dev versions to production with brickr deploy prod |

Runtime contract

If you are writing or generating Brickr custom code, use this as the execution contract:

| Topic | Contract | |-------|----------| | Target runtime | Deployed code nodes run inside Brickr's Cloudflare Worker-based runtime, not in a Node.js server | | Authoring model | Write portable Worker-style JavaScript / TypeScript, not Node.js module code | | Node.js built-ins | Do not rely on fs, path, net, child_process, require, or process.env in deployed nodes | | Execution-engine internals | Your node does not get direct access to the raw Worker env, service bindings, D1, KV, R2, or other internal platform bindings | | Secrets | Use ctx.secret("NAME") for workspace secrets | | Shared types | Define enums and structures in brickr.config.ts, then reference them from node pins | | Reliable output shape | run(ctx) should return a plain object that matches the declared output pins |

The intended model is: Worker-style code, Brickr-managed secrets and types, and no backdoor access to the surrounding execution engine.

brickr dev runs in your local Node.js process for convenience. Deployed code nodes do not get that full environment. Treat local dev as a fast feedback loop, not as the runtime contract.

What a custom code node is

A custom code node is a normal Builder node backed by source code. It has:

  • A stable nodeKey
  • A Builder-facing name, description, and category
  • Typed input and output pins
  • A run(ctx) function that returns the output object

Once published to your workspace, the node appears in the Builder like any other node. You can search for it, place it on the canvas, wire it into flows, and version it through the dev/prod deployment model.

The project shape

Running brickr init creates a small local project for code nodes:

my-project/
  .brickr/
    project.json
  package.json
  brickr.config.ts
  src/
    nodes/
      hello-world.br.ts

Each file has a specific role:

| File | Purpose | |------|---------| | .brickr/project.json | Stores the linked workspace, auth token, language, and connection metadata | | package.json | Project dependencies, including @brickr/sdk | | brickr.config.ts | Code-managed enums, structures, and secrets for this workspace | | src/nodes/*.br.ts | Your actual custom code nodes |

project.json contains local connection credentials. Treat it like a machine-local file and avoid committing live tokens to version control.

The end-to-end workflow

Use brickr init or simply npx brickr in an empty directory. The CLI opens Brickr in the browser, lets you connect a workspace, scaffolds the project, and installs @brickr/sdk.

2. Create node files

Custom code nodes live in src/**/*.br.ts or src/**/*.br.js. The .br.* suffix is how the CLI discovers them.

Minimal example:

import { defineCodeFunctionNode, t } from "@brickr/sdk";

export default defineCodeFunctionNode({
  nodeKey: "main.hello-world",
  name: "Hello World",
  description: "Example custom code node",
  category: "code-function",
  inputs: {
    name: t.string().default("World")
  },
  outputs: {
    message: t.string()
  },
  async run(ctx) {
    return {
      message: `Hello ${ctx.inputs.name as string}`
    };
  }
});

3. Test locally with brickr dev

brickr dev does not deploy. It bundles your local node files, generates test inputs from defaults and pin types, executes the nodes on your machine, prints outputs, and keeps watching for file changes.

If you only want one node, pass a selector:

brickr dev main.hello-world

4. Publish to the workspace dev channel

When local behavior looks correct, publish the latest node bundles to your linked workspace:

brickr deploy dev

This makes the current node versions available in Brickr's dev environment so you can use and test them in the Builder.

5. Promote to production

After validating the dev versions, promote them:

brickr deploy prod

This keeps the dev and production lifecycle explicit. Local edits stay local until you publish them, and dev versions stay out of production until you promote them.

Code-managed assets

The SDK does not only define nodes. It also lets you manage workspace-level assets in code:

  • Enums
  • Structures
  • Secrets

These live in brickr.config.ts and sync with:

brickr assets sync

This is useful when your code nodes depend on a stable shared type like OrderStatus, HttpHeader, or a required secret such as STRIPE_API_KEY.

That gives custom code nodes a clean source-of-truth model:

  • pin contracts come from the node definition
  • shared enums and structures come from brickr.config.ts
  • secrets come from the workspace secret store through ctx.secret(...)
  • runtime metadata comes from Brickr's controlled ctx.env, not the raw execution engine

If Brickr detects that a code-managed enum, structure, or secret was changed in the UI and now differs from your local source, brickr status warns about asset drift and points you to brickr assets sync.

Two ways to create code nodes

Local CLI workflow

The classic path: write .br.ts files locally, test with brickr dev, deploy with brickr deploy. Full control, full TypeScript, local iteration loop.

AI-generated nodes in the Builder

The Builder AI can generate code nodes for you when you describe what you need. The AI:

1. checks existing workspace code nodes first 2. generates a new node only if nothing suitable exists 3. saves it to your workspace immediately 4. wires it into the route it is building

After the AI creates a node, it gives you a command to pull the source locally:

npx brickr open <nodeId>

This writes the node as a proper .br.ts file in src/nodes/. You can then edit it in your editor and push changes with brickr deploy — the same as any hand-authored node.

Both paths produce the same kind of node. An AI-generated node and a locally authored node are identical once deployed.

When to use custom code

Use custom code nodes when:

  • You need logic that does not exist in the built-in node library
  • You want to wrap a third-party SDK or API pattern behind a reusable node
  • You want stronger version control than an in-browser editor alone provides
  • You want local iteration before publishing to the cloud

Stay with visual Builder nodes when:

  • The logic is already expressible in the built-in node graph
  • The node is primarily a reusable visual composition
  • The team working on it does not need a local code workflow

The cleanest setup is:

1. Keep node source files focused and small. 2. Keep nodeKey stable over time. 3. Test with brickr dev before every publish. 4. Publish with brickr deploy dev. 5. Promote with brickr deploy prod only after Builder testing. 6. Keep enums, structures, and secrets in brickr.config.ts so the workspace stays reproducible.

What's next?

| Topic | Description | |-------|-------------| | CLI | Full command reference and linked-workspace rules | | SDK | @brickr/sdk API, types, and helpers | | Workflow | Recommended authoring, testing, deploy, and troubleshooting flow | | Custom Nodes | Relationship between legacy custom nodes and the new local custom code system |