Configure an Instance from a Generic node
Bind a Generic node's port to a real type in the console wizard or with axiom instance create — author nested message-typed fields, toggle repeated/optional, and preview a mapping's exact output before you save.
View as MarkdownAn Instance binds a
Generic node's port(s) to a real,
specific message type. The shortest path — one flat, scalar-only message per
port — is covered in axiom instance create
and the marketplace's Generic Nodes tab. This guide covers the richer
authoring model both surfaces also support: a field whose type is itself
another message, defined inline and recursively; repeated/optional
field modifiers; and previewing a mapping's exact output before you commit
to a shape.
Nothing you author while configuring an Instance becomes a real, queryable
registry message until you save (console) or run axiom instance create
(CLI) for the whole Instance. Closing the wizard, hitting Cancel at any
nesting level, or a failed create leaves nothing behind — every nested
message you draft is synthesized transactionally, in the same request that
creates the Instance, never as a separate call per message.
Prerequisites
- You are logged in to the Axiom app, and the CLI has run
axiom login(Installation). - A published Generic node exists to instantiate — a package with
kind: genericand at least onegeneric_portsentry (see Generic and Instance nodes). Browse one via the marketplace's Generic Nodes tab.
Configure an Instance in the console
Open the wizard from the marketplace's Generic Nodes tab (Create
Instance on a node's detail page) or by navigating to /instances/new.
Each generic port (output, input, or both) gets its own column with two
steps: Select message, then Map fields.
Author a message with nested fields
In Step 1, Create new message opens a form: a message name, then a list
of fields. Each field has a name and a type — either a scalar (string,
int32, int64, uint32, uint64, double, float, bool, bytes) or
message, a reference to another message.
Picking message opens a picker with its own Create new message
button — authoring a nested message reuses the exact same form, recursively.
There is no fixed depth limit in the UI; author as many levels as the shape
actually needs (the server enforces a generous cap — see
Limits on a drafted message tree
below). Today, a nested field's message type can only be an inline,
freshly authored message — picking an already-published message as a nested
field's type is not yet supported end to end (it would carry no field
definitions of its own, and the request is rejected). Author the nested
shape inline instead.
Each field also has independent repeated and optional toggles.
They're mutually exclusive — proto3 forbids repeated optional — so
turning one on clears the other.
A Proto preview toggle on the form shows the exact .proto text your
current name/fields/modifiers produce, live, recomputed on every keystroke —
useful for confirming a nested shape's rendered syntax before committing.
Map fields and preview the mapping
Step 2 renders the same field-mapping panel used for ordinary flow edges, so target fields bind to source values by name, CEL expression, or literal. Its collapsible Preview section runs your mapping against sample JSON you type per source, showing the exact value it currently produces or the mapping error blocking it — the same engine the registry and worker use at real compile/dispatch time, not an approximation. This works identically whether every message involved is already real or one is a message you just authored in Step 1: an as-yet-undrafted message is sent to the preview endpoint as an inline definition instead of a registry name, so you can iterate on a brand-new nested shape and see real output before anything is saved.
Name and save
Once every declared generic port shows resolved, name the Instance
(your-handle/instance-name) and click Save instance. The whole
draft tree — the top-level facade for each port plus every nested message,
however deep — is synthesized in one transaction; a validation failure at
any level leaves nothing behind.
Configure an Instance with the CLI
axiom instance create supports the same two authoring shapes as the
console, chosen per port.
Flat fields (the common case)
--output-field/--input-field name=kind (repeatable) covers a flat,
scalar-only message — see
axiom instance create for the
full flag reference. This shape is unchanged and always available.
Nested fields with a fields-file
For repeated/optional modifiers, or a field whose type is another message
(recursively), pass --output-fields-file/--input-fields-file instead: a
path to a JSON file shaped {"message_name": "...", "fields": {...}}. Each
value in fields is either:
- a bare scalar-kind string (
"total": "double") — identical to the flat shape, and - a JSON object for anything richer:
{"kind": "<scalar>", "repeated": true},{"kind": "<scalar>", "optional": true}, or{"kind": "message", "message": {"message_name": "...", "fields": {...}}}for a nested, not-yet-real message.
--output-fields-file is mutually exclusive with --output-message/
--output-field on the same port — pick one shape of input per port.
--input-fields-file is likewise exclusive with --input-message/
--input-field. The two ports are independent: the output port can use a
fields-file while the input port uses flat flags, or vice versa.
A worked example — an OrderSummary output facade with a top-level
repeated field and a nested Customer message:
{
"message_name": "OrderSummary",
"fields": {
"id": "int64",
"tags": { "kind": "string", "repeated": true },
"customer": {
"kind": "message",
"message": {
"message_name": "Customer",
"fields": {
"name": "string",
"age": { "kind": "int32", "optional": true }
}
}
}
}
}Save that as order-summary.fields.json and pass it in:
axiom instance create acme/orders-query \
--generic-package acme/postgres-query --generic-node "Postgres Query" \
--output-fields-file ./order-summary.fields.jsonOrderSummary and Customer are both synthesized in the same request that
creates acme/orders-query — neither exists as a registry row if the
request fails validation at any level.
Preview a mapping before you create
axiom instance preview wraps POST /adapters/preview verbatim — the same
engine the registry compiler and worker use at real dispatch time, run
against sample JSON you supply, with no live invoke. It reads a request body
from --request-file (or stdin):
{
"dst_msg_name": "OrderSummary",
"sources": [
{"edge_id": "e1", "msg_name": "Order", "sample": {"id": 1, "amount": 42.5}}
],
"adapter": {"total": "$.amount"}
}Exactly one of adapter (a single-edge mapping) or compose_mapping (a
multi-source compose consumer) is required. Any msg_name slot —
dst_msg_name, or a sources[] entry — accepts a bare registry message name
(a JSON string, as above) or an inline draft in place of it: the
identical {"message_name": ..., "fields": {...}} shape the fields-file
above uses, letting you preview a mapping against a message you haven't
created yet:
{
"dst_msg_name": {"message_name": "OrderSummary", "fields": {"total": "double"}},
"sources": [{"edge_id": "e1", "msg_name": "Order", "sample": {"amount": 42.5}}],
"adapter": {"total": "$.amount"}
}axiom instance preview --request-file ./preview-request.jsonA mapping-level failure (bad expression, type mismatch, unresolvable field)
prints as an error and exits non-zero — it's a property of the mapping
you're iterating on, not a malformed request. Once the output looks right,
reuse the exact same message shape as the fields-file input to
axiom instance create. See
axiom instance preview for
the full flag reference.
Limits on a drafted message tree
Both surfaces synthesize (console Save) or accept (CLI preview/create) a draft tree under the same bounds, so a request fails fast with a clear error instead of an unbounded compile:
| Limit | Bound |
|---|---|
| Nesting depth (top-level message counts as depth 1) | 10 |
| Distinct messages in one tree (both ports combined) | 100 |
| Fields on any one message | 200 |
| Request body size | 1 MiB |
A name reused for two different shapes within one request is rejected as a collision; drafting the identical name and shape twice (for example, the same nested message referenced from both the output and input port) is a harmless dedup, not an error.