Custom Node Development
FluidGrids nodes are self-contained microservices. Each node lives under nodes/<node>/<version>/ in the fluidgrids-nodes repository and exposes a small GraphQL API that the workflow engine invokes during a run. This guide covers creating, testing, and deploying one.
There is no @fluidgrids/node-sdk package and no defineNode() helper — a node is a directory of files, not a compiled bundle. The canonical reference is nodes/gmail_node/1.0.0; copy its structure for every new node. The full contract is documented in docs/node-standard.md.
Node Architecture
A FluidGrids node consists of:
manifest.json— the contract: node identity (nodeType,nodeVersion,publishType,endpointUrl,healthCheckUrl, category, icon) plus the fulloperations[]list- GraphQL SDL (
<domain>_sdl.gql) — one field per operation - Ariadne resolvers (
<domain>_gql.py) — binds every SDL field - Handlers (
<domain>_actions_ops.py) — the code that runs when an operation executes, wrapped in a FastAPI app (server.py+router.py) - A per-operation configuration panel — an AdaptiveCard 1.5
adaptive_carddeclared on each operation, which the builder renders as that operation's property panel - Error handling — resolvers never raise; every result type carries an
error: Stringfield
Integration nodes are Python (FastAPI + Ariadne); a handful of platform nodes use Bun/TypeScript but keep the same contract (manifest shape, GraphQL signature, health endpoint, port, network, per-execution credential injection).
Development Process
Setup
# Clone the nodes repo
git clone https://github.com/algoshred/fluidgrids-nodes.git
cd fluidgrids-nodes
Scaffold the node
Create nodes/<node>/<version>/ and copy the file set from the reference node nodes/gmail_node/1.0.0/:
nodes/my_node/1.0.0/
├── manifest.json # the contract: identity + operations[]
├── my_node_sdl.gql # GraphQL SDL — one field per operation
├── my_node_gql.py # Ariadne resolvers
├── my_node_actions_ops.py # handlers — the real work
├── server.py # FastAPI app: /graphql + /api/v1/health
├── router.py # REST /health route
├── config.py # Pydantic Settings from .env (PROJECT_NAME, APP_PORT, ...)
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
└── .env
Each operation in manifest.json must be backed by an SDL field, a resolver, and a handler — no manifest-only operations. Every operation also carries an adaptive_card (AdaptiveCard 1.5) that becomes its configuration panel in the builder, and a credentialAdapters list so the engine can inject credentials per execution. Credentials are never stored in the node.
Run and test locally
Each node ships its own docker-compose.yml. Bring the container up from the node directory — it hot-reloads your source and joins the shared external nodenetwork:
cd nodes/my_node/1.0.0
docker compose up -d
# Health check
curl localhost:<APP_PORT>/api/v1/health
# GraphQL playground
open http://localhost:<APP_PORT>/graphql
APP_PORT is unique per node (Gmail uses 6501) and set in the node's .env. The nodenetwork is created by another compose stack; if it does not exist yet, create it with docker network create nodenetwork.
Register the node locally
Re-seed the local node registry so the builder can see your node. seed-nodes.ts reads every manifest.json in the repo and upserts it into the local registry database:
cd fluidgrids-nodes
bun scripts/seed-nodes.ts
Reload the builder and your node appears in the palette.
Deploying a node
Deployment is a container image plus a registry entry — there is no web-app upload and no fluidgrids nodes publish command.
-
Build the image.
scripts/build-node-image.shreads the node'smanifest.jsonand tags the image by the convention the workers' sandbox mode expects (${NODE_IMAGE_REGISTRY_PREFIX}/${nodeType}:${version}):# Build locally
scripts/build-node-image.sh my_node 1.0.0
# Build and push to the registry
# <registry-host> = your environment's container registry (see the fluidgrids-nodes repo)
NODE_IMAGE_REGISTRY_PREFIX=<registry-host>/fluidgrids-nodes \
scripts/build-node-image.sh my_node 1.0.0 --push -
Seed the registry. Run
scripts/seed-nodes.tsagainst the target environment's registry so the manifest is registered. In alpha and production this flows through the standard node deploy pipeline (per-node images built and published via.github/workflows/build-node-images.yml, then registry seeding).
Browsing the catalog
The CLI can browse the node catalog but does not deploy nodes:
fluidgrids nodes list # list available nodes
fluidgrids nodes get <type> # inspect a node's manifest
Next steps
- Node Property Panels — building rich configuration panels
- Examples — sample workflows and nodes