Modeling Node
Why detail Mycel's internal models?
While your client only interacts with JSON payloads, understanding Mycel's internal data structure is highly recommended. Conceptually mirroring this architecture in your client will help you when handling polymorphism, deserialization, and local caching. See all Mycel's Models and Schemas for more details.
Both Spore and Fragment Learning Unit extend from the class BaseLearningUnit.
When fetching nodes, the API condenses this internal structure (Node + attached Learning Units) into specific views to optimize payload size. Depending on the endpoint, it returns either:
- NodeView — A lightweight model used for lists and searches. It contains node metadata and attached learning units, but strips the full content in favor of a short content_preview.
- NodeDetailView — The complete model. It inherits from NodeView but also includes the full fields dictionary (the complete text content). Used when loading a specific node for editing or review.
In both cases, Nodes always comes with their full LearningUnitView: FragmentView or SporeView.
Visualize that in the API
For instance, see in the API reference that List Nodes returns a list of NodeView objects, while Get Node returns a NodeDetailView. In Scalar, in the learning_units field, you can select a LearningUnitView (SporeView or FragmentView).
Suggested structure
See Mycelium example.
Unified Node model
Instead of creating two distinct models for NodeView and NodeDetailView, combine them into a single Node model:
- Make all fields required except for those that are optional in
NodeView. - Add all fields from
NodeDetailView.
That way, whether mycel sends a lightweight NodeView or a complete NodeDetailView, your Node model handles it — and the cache is centralized.
Use polymorphism for Learning Units
Since a single Node can contain different types of learning units (Spores or Fragments), your models need to handle polymorphism.
While the API returns a flat JSON object for a learning unit, you must decide how to structure the shared fields (like due, slot, or priority) versus the type-specific fields (like learning_data or dismiss) in your code. Depending on your language constraints, you generally have two choices:
- Inheritance: Spore and Fragment directly extend a BaseLearningUnit class that holds all the common fields.
- Composition (chosen architecture for Mycelium): You separate the data storage from the typing. Spore and Fragment internally contain a BaseLearningUnit instance which strictly holds the shared data, while declaring their specific fields in their own classes. A generic LearningUnit interface allows grouping both types in a single List
.
Deserialization by type
Since the Node's learning_units field can contain either FragmentView or SporeView, you need to deserialize based on a type discriminator in the JSON. See the "switch" pattern in the Mycelium example in the factory of the sealed class LearningUnit.
Secondary models
You can also create models for NodeType, NodeData, NodeFields, etc., but it is less necessary — the unified Node model already covers the main use cases.
For endpoints that return formatted data rather than full nodes (such as "Get Priorities" or "Create Node Extract"), you may create dedicated models. In Mycelium, the "Get Priorities" response is parsed directly and saved into NodeCache without passing through an additional model.
With this structure in place, you are now ready to load nodes.