# Requiring a msg structure - enforcing the presence of fields and attributes

**URL:** <https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432>\
**Category:** General\
**Created:** [19 September 2023 09:16 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432 "2023-09-19T09:16:40Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 09:16 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/1 "2023-09-19T09:16:40Z")

</div>

Hi There!

I'm sure this question has been posed many times before but I didn't find anything quickly - therefore: sorry for the repetition!

The core of the question is: is there a way to define the _structure_ of a message and have a node check/enforce that structure?

Example is this [flow which _expects_ certain fields](https://flowhub.org/f/90196166b57a77e5) to be defined on the `msg` object. To describe that, I have a code snippet but no node in the flow that enforces that requirement:

```auto
msg.email = {
    /* addresses */
    from: "sender@example.org",
    from_name: "Example Robot",
    to: "recipient@example.org",
    cc: false,
    bcc: false,

    [... rest ignored ...]
}

```

I'm not a particular fan of typed systems but for pre-defined flows it would be great to have a clear _interface_ for flow usage.

This question is less about typing (integer,string,float) and more about structure ("object requires field XYZ"). In the above example, I'm less worried whether the field 'cc' is boolean or string, rather that the field 'cc' is defined on the attribute `email` on the object `msg`.

Cheers!

---

<div class="post-metadata">

**Author:** ![zenofmud](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/zenofmud/32/316_2.png) [@zenofmud](https://discourse.nodered.org/u/zenofmud)\
**Post date:** [19 September 2023 09:35 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/2 "2023-09-19T09:35:39Z")

</div>

If you use a switch you can set the test to the `Property' to the part you are looking for and then check 'is not null' and if it exists it will be true otherwise it s false. Here is a sample flow:

```auto
[{"id":"5f9754f292889ff1","type":"function","z":"be8d973fa360b639","name":"function 1","func":"msg.email = {\n /* addresses */\n from: \"sender@example.org\",\n from_name: \"Example Robot\",\n to: \"recipient@example.org\",\n cc: false,\n bcc: false,\n}\nreturn msg","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":320,"y":160,"wires":[["24d30be3b19938c9","1ba1a58c17708352"]]},{"id":"7e67b700f1909d6e","type":"inject","z":"be8d973fa360b639","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":140,"y":160,"wires":[["5f9754f292889ff1"]]},{"id":"24d30be3b19938c9","type":"switch","z":"be8d973fa360b639","name":"check for msg.email.from_name","property":"email.from_name","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":550,"y":100,"wires":[["5b595dc12c7b2387"],["ce50aabefa77d7a5"]]},{"id":"5b595dc12c7b2387","type":"debug","z":"be8d973fa360b639","name":"exists1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":80,"wires":[]},{"id":"ce50aabefa77d7a5","type":"debug","z":"be8d973fa360b639","name":"not exists1","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":790,"y":120,"wires":[]},{"id":"1ba1a58c17708352","type":"switch","z":"be8d973fa360b639","name":"check for msg.email.meal","property":"email.meal","propertyType":"msg","rules":[{"t":"nnull"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":550,"y":200,"wires":[["dde6840fc2ca6993"],["68da4150f0f773e4"]]},{"id":"dde6840fc2ca6993","type":"debug","z":"be8d973fa360b639","name":"exists2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":780,"y":180,"wires":[]},{"id":"68da4150f0f773e4","type":"debug","z":"be8d973fa360b639","name":"not exists2","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":790,"y":220,"wires":[]}]

```

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 10:03 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/3 "2023-09-19T10:03:03Z")

</div>

> [@zenofmud](#):
>
> If you use a switch you can set the test to the `Property' to the part

Good idea but that's a switch per property plus the overall structure does not become clear because there are so many individual switch. I admit I have no better solution and a series of switches would definitely do the job.

Of course I could also just generate those switches based on a structure that I define .... _evil laugh_.oO(dynamically generating a working flow in the frontend ... it does feel like UML and the early 2000s)

---

<div class="post-metadata">

**Author:** ![kevinGodell](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/kevingodell/32/27040_2.png) [@kevinGodell](https://discourse.nodered.org/u/kevinGodell)\
**Post date:** [19 September 2023 10:07 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/4 "2023-09-19T10:07:11Z")

</div>

If you want to get complicated, you could define a class `MyEmailClass` that has those values set in the constructor where you would do the validation on the values to ensure they are set. Later, you could check that your object instance is `myEmailObj instanceof MyEmailClass`. The class would have to be accessible on wherever you create an instance and then later check the instanceof. That should be able to be set in the global context, but I have not tried.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 10:20 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/5 "2023-09-19T10:20:07Z")

</div>

> [@kevinGodell](#):
>
> If you want to get complicated, you could define a class `MyEmailClass`

Wait what?!?! 🙂 I can define classes in Node-RED? OMGosh, this is really getting very UML'isk - I remember generating Java classes from UML specifications - mind blowing stuff at the time but a real pain to code against - infinitely long class names resulted with infinitely long method names ...

But yes, what I am talking about are definitely Java interfaces 2.1!

---

<div class="post-metadata">

**Author:** ![E1cid](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/e1cid/32/77971_2.png) [@E1cid](https://discourse.nodered.org/u/E1cid)\
**Post date:** [19 September 2023 10:38 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/6 "2023-09-19T10:38:17Z")

</div>

Just for info  
You could use JSON schema in the JSON node to validate properties and values.

> **[JSON Schema Validation: A Vocabulary for Structural Validation of JSON](https://json-schema.org/draft/2020-12/json-schema-validation.html)**
>
> JSON Schema (application/schema+json) has several purposes, one of which is JSON
> instance validation.
> This document specifies a vocabulary for JSON Schema to describe the meaning of JSON
> ...

---

<div class="post-metadata">

**Author:** ![Colin](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/colin/32/17040_2.png) [@Colin](https://discourse.nodered.org/u/Colin)\
**Post date:** [19 September 2023 10:38 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/7 "2023-09-19T10:38:23Z")

</div>

> [@gregorius](#):
>
> Wait what?!?! 🙂 I can define classes in Node-RED?

Javascript supports classes so yes, you can define classes in a function node.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 12:46 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/8 "2023-09-19T12:46:32Z")

</div>

> [@E1cid](#):
>
> You could use JSON schema in the JSON

I recently used the [swagger nodes](https://flows.nodered.org/node/node-red-node-swagger) they do a really good job in defining an API interface. Perhaps there is a way to use them to make a formal definition for the requirements of a flow...

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [19 September 2023 13:28 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/9 "2023-09-19T13:28:53Z")

</div>

AsyncAPI ([https://www.asyncapi.com](https://www.asyncapi.com)) may be a better match than OpenAPI (Swagger)  
and as already mentioned there are some JSON schema checking nodes in the flows library also.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 13:37 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/10 "2023-09-19T13:37:10Z")

</div>

I just found [json-multi-schema nodes](https://flows.nodered.org/node/node-red-contrib-json-multi-schema) - I was going to give them a whirl on the FJ....

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [19 September 2023 14:28 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/11 "2023-09-19T14:28:24Z")

</div>

A basic validator is also built into the core JSON node -

 ![image](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/e/c/ec743334a665bdfd76f482a01005d8ae04116670.png)

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 14:40 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/12 "2023-09-19T14:40:30Z")

</div>

> [@dceejay](#):
>
> A basic validator is also built into the core JSON node

Noooooooooo ☹ I just created a [flow](https://flowhub.org/f/36690f145d5af6ca) using the [full-msg-json-schema-validation](https://flows.nodered.org/node/node-red-contrib-full-msg-json-schema-validation) node that does exactly what I wanted - doh!

But I'll update that presently with these new details! Cheers 👍

---

<div class="post-metadata">

**Author:** ![E1cid](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/e1cid/32/77971_2.png) [@E1cid](https://discourse.nodered.org/u/E1cid)\
**Post date:** [19 September 2023 14:43 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/13 "2023-09-19T14:43:00Z")

</div>

> [@gregorius](#):
>
> Noooooooooo ☹

You should know by now, you should always read the node help text. (some witty smiley that i can never bothered to use)

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 14:47 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/14 "2023-09-19T14:47:26Z")

</div>

> [@E1cid](#):
>
> You know by now you should always read the node help text

RTFM? Hm I always wondered what that meant ....

In my defensive, I was not thinking of converting my payload to JSON (using a JSON node) so that a JSON node (but another JSON node) would parse it with a JSON schema (the JSON schema being in a template node) to tell me that the original payload was ok! 😉 That is a little too creative even for me!

---

<div class="post-metadata">

**Author:** ![E1cid](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/e1cid/32/77971_2.png) [@E1cid](https://discourse.nodered.org/u/E1cid)\
**Post date:** [19 September 2023 14:50 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/15 "2023-09-19T14:50:19Z")

</div>

In your defense, But you are industrious and get there in the end.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 14:52 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/16 "2023-09-19T14:52:35Z")

</div>

> [@E1cid](#):
>
> get there in the end.

most of the time, the end is just the beginning of something else ... ends seem to be blind corners.

but in the end, it's the journey not arrival that counts.

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [19 September 2023 16:54 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/17 "2023-09-19T16:54:26Z")

</div>

Turns out that using the JSON node is just as good even if there is an extra conversion step - [updated flow](https://flowhub.org/f/36690f145d5af6ca).

Definitely solves my intended use-case and will make, imho, sharing code simpler since its possible to define clear requirements on the `msg` object.

---

<div class="post-metadata">

**Author:** ![dceejay](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/dceejay/32/38_2.png) [@dceejay](https://discourse.nodered.org/u/dceejay)\
**Post date:** [19 September 2023 21:22 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/18 "2023-09-19T21:22:38Z")

</div>

> [@gregorius](#):
>
> extra conversion step

Hmm. Maybe there is an enhancement to the json node required to add a mode where it just checks schema and doesn’t convert ?

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [20 September 2023 08:31 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/19 "2023-09-20T08:31:43Z")

</div>

> [@dceejay](#):
>
> checks schema and doesn’t convert ?

Isn't that a separate validation node? It might be confusing to add that functionality to the JSON node - I dunno.

I assume it would be a fourth option here:

![Screen Shot 2023-09-20 at 10.19.43](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/a/2/a22b41e0f34ecaddefd5e03e26927cbd1ba07f26.png)

Something like "Validate and maintain format"

Since the node does validation by default, emphasis should be placed on maintaining the format, i.e. "Validate only" would be confusing since it does not convey the purpose of the option.

EDIT:

If the JSON node does have a "validate only mode" then it should be possible to include the schema in the node itself, like the msg-object-...tion node above. That way the JSON node can act as a gateway to the usage of a flow and it would only be one node (instead having a template node containing the schema and passing it on to the json node).

---

<div class="post-metadata">

**Author:** ![gregorius](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/gregorius/32/73816_2.png) [@gregorius](https://discourse.nodered.org/u/gregorius)\
**Post date:** [21 September 2023 16:55 UTC](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432/20 "2023-09-21T16:55:24Z")

</div>

Just for those that come after me: I ended rolling my own validator function node because I wanted to check attributes on the `msg` object. This isn't possible with the either the JSON node nor the msg-validator node because they can only validate a _property_ of the `msg` object.

So my request would be that if the JSON node is extended as a validator, then please allow validation on the top-level `msg` object and not just on a property of the `msg`.

This is a simple example of a schema and the home-grown function node:

```auto
[{"id":"9d516b0b284e8481","type":"template","z":"36690f145d5af6ca","name":"schema","field":"schema","fieldType":"msg","format":"json","syntax":"plain","template":"{\n \"title\": \"Email data required on the msg object\",\n \"type\": \"object\",\n \"required\": [\n \"email\"\n],\n \"properties\": {\n \"email\": {\n \"type\": \"object\",\n \"required\": [\n \"to\",\n \"from\",\n \"subject\"\n],\n \"properties\": {\n \"from\": {\n \"type\": \"string\",\n \"description\": \"Sender email.\"\n },\n \"from_name\": {\n \"type\": \"string\",\n \"description\": \"Senders full name.\"\n },\n \"to\": {\n \"type\": \"string\",\n \"description\": \"Receiver email.\"\n },\n \"cc\": {\n \"type\": \"string\",\n \"description\": \"CC email or blank.\"\n },\n \"bcc\": {\n \"type\": \"string\",\n \"description\": \"BCC email or blank.\"\n },\n \"subject\": {\n \"type\": \"string\",\n \"description\": \"Subject Line of email\"\n }\n }\n }\n }\n}","output":"json","x":372,"y":1091,"wires":[["9efdc1c898e037ec","62a6aee706773c33"]]},{"id":"a14424b157213fb4","type":"function","z":"36690f145d5af6ca","name":"JSON Schema validator","func":"var validator = new Ajv({\n allErrors: true,\n messages: true\n})\n\nconst validate = validator.compile(msg.schema)\n\nconst v = validate(msg);\n\nif ( !v ) {\n msg.errors = validate.errors;\n node.error(\"validation failed\", msg)\n} else {\n delete msg.schema;\n return msg;\n}","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[{"var":"Ajv","module":"ajv"}],"x":774,"y":1091,"wires":[["88dad74cd25d507d"]],"outputLabels":["ok"]},{"id":"9efdc1c898e037ec","type":"function","z":"36690f145d5af6ca","name":"valid data","func":"msg.email = {\n cc: \"someemail\",\n to: \"asdsa\",\n from: \"dddd\",\n subject: \"hello world\"\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":544,"y":1091,"wires":[["a14424b157213fb4"]]},{"id":"7eb33583b79df7f6","type":"inject","z":"36690f145d5af6ca","name":"Trigger","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":187,"y":1091,"wires":[["9d516b0b284e8481"]]},{"id":"88dad74cd25d507d","type":"debug","z":"36690f145d5af6ca","name":"msg is good","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"email","targetType":"msg","statusVal":"","statusType":"auto","x":1063,"y":1092,"wires":[]},{"id":"036e12f9db5b7590","type":"catch","z":"36690f145d5af6ca","name":"","scope":["a14424b157213fb4"],"uncaught":false,"x":803,"y":1124,"wires":[["256142c98e44b3d0"]]},{"id":"256142c98e44b3d0","type":"debug","z":"36690f145d5af6ca","name":"msg is bad","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"errors","targetType":"msg","statusVal":"","statusType":"auto","x":1064,"y":1125,"wires":[]},{"id":"62a6aee706773c33","type":"function","z":"36690f145d5af6ca","name":"invalid data","func":"msg.email = {\n cc: \"someemail\",\n to: \"asdsa\",\n}\n\nreturn msg;","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":557,"y":1148,"wires":[["a14424b157213fb4"]]}]

```

[Next page](https://discourse.nodered.org/t/requiring-a-msg-structure-enforcing-the-presence-of-fields-and-attributes/81432.md?page=2)
