Models: parsing and breaking down data

Pipelines often need to work with structured data — a webhook payload, an API response, a file. Flovello represents that structured data as a model: a named schema you define (an object with fields, or an enum), which flows through your pipeline as a single typed value. This tutorial covers the handful of nodes that create, split apart, and serialise models.

What's a model?

A model is a schema you create under Models in the app — a named set of typed fields, which can nest objects and arrays. Here's the Fields editor on an "Order" model's page once defined, with a customerId, a list of line items, and a total:

Fields Add field
customerIdString Non-nullable, required
itemsArray Non-nullable, default empty
Item typeObject
Array item fields Add

nameString Non-nullable, required
quantityInteger Non-nullable, default valueValue

totalDecimal Non-nullable, required
noteString Nullable, default null

Once defined, a model behaves like any other data type: pins can carry a single model value between nodes, typed to that specific model. The rest of this tutorial uses this "Order" model as its running example.

What happens when a field is missing

The dropdown beside each field's type is the one decision that shapes how the rest of your pipeline has to be built. It answers two questions at once, which is why every option names both.

Nullable or non-nullable — is null a value this field accepts at all? A non-nullable field is a promise: it can never hold null, so nothing downstream has to defend against one.

Required, or what it defaults to — what happens when the incoming data doesn't supply the field?

  • Required — parsing fails with an error naming the field. For data you cannot proceed without, like customerId above.
  • Default null — the field becomes null. Only available on a nullable field, since filling null while refusing null is a contradiction.
  • Default empty — for arrays and objects: an empty list, or an object whose own fields are filled by their own rules, all the way down. An items array that defaults to empty means the loop over it runs zero times instead of needing a guard.
  • Default value — for text, numbers, booleans and enum references: the literal you enter. quantity above defaults to 1, so arithmetic downstream never has to cope with a missing number.

The nullability half matters because omitting a key and explicitly sending null are two different messages from whoever is calling you:

{ } (key absent) { "quantity": null }
Non-nullable, default 1 1 1 — null isn't accepted, so the default answers
Nullable, default 1 1 null — the sender meant it, so it's kept
Nullable, required fails null
Non-nullable, required fails fails

Choosing non-nullable is what buys the guarantee. Reach for nullable when data is genuinely, meaningfully absent — every pin carrying a nullable value is marked with a ? in the editor, at both ends of every connection, so you can see exactly how far a possible null travels, and Is null? and Default if null are there to handle it.

The default also applies when you build a value with Make rather than parse one. A Make pin you leave untouched shows what the model will fill in greyed text — null, [], 1 — instead of a value it isn't going to produce. Click it to set something else, or clear it to go back to the model's default. A required field is the one case Make can't fill for you, so leaving its pin empty raises a warning on the node.

Parsing raw input into a model

Raw input — a request body, a file, an API response — usually arrives as a stream of bytes, not a model. The Parse to model node bridges that gap: pick a model in the inspector and a wire format (JSON, YAML, or XML), and it parses the stream into a typed value of that model. Because parsing can fail on malformed input, Parse to model sits on the execution flow with two branches — success and error — so you can handle bad input explicitly. When the input is a list rather than a single object, an array variant parses the stream into an array of the chosen model instead.

Breaking a model into fields

Once you have a model value, Break splits it apart. Pick the model in the inspector and the node grows one output pin per field, each typed to match — a string field becomes a string pin, a nested model field becomes another model pin you can Break again. Break is pure: it has no execution pins, and its outputs are computed lazily, only when something downstream reads them.

Composing a model from fields

Make is Break's counterpart: pick a model in the inspector and it grows one input pin per field, then emits the assembled model on its value output. Make is also pure — the model is assembled lazily, only when something reads it. Use it whenever a downstream node expects a whole model rather than loose fields, such as before building a request body.

Serialising a model back out

To turn a model back into text or bytes, use Model to string or Model to stream. Both are pure and support JSON, YAML, and XML. Reach for Model to string when you just need the text — for example to log a model with Log message. Reach for Model to stream when the destination is a streaming consumer, such as the body input of an HTTP request — it writes the serialised model straight to the socket without buffering the whole payload as a string first.

Worked example: reading fields from an order webhook

Here's what it looks like to receive an order webhook, parse its body into an "Order" model, and break that model apart into individual fields:

Receive HTTP
Method (HTTP method)
Headers (HTTP headers)
Body (Input stream)
Parse to Order
Success
Input (Input stream)Model (Order)
Format (Data format)JSONError
Break Order
Value (Order)customerId
items
total

From there, wire customerId into a Log message node. You don't need to wire anything to the Error branch: an unwired execution output already fails the pipeline automatically, so malformed webhooks are surfaced by default. Only wire a Fail node there if you want a more specific failure message than the default.

See also