What is a Resource?#
Introduction#
Resources in Malevich Nova are Python classes that describe a subgraph in the database. You declare a resource by subclassing Resource and adding fields that describe the main node (pivot) and its relationships (mounts). Resource enables you to create a portion of the graph atomically and retrieve larger chunks of data in a single query. Also, Resources may dictate the rules to access the data and provide custom business logic.
Concepts#
Pivot: The main node of your resource. Think of it as the root or entry point.
Mount: Use when the edge (relationship) is declared on the pivot node.
ForeignMount: Use when the edge is declared on another node, but you want to access related nodes from the perspective of your pivot.
ResourceEdge: A small container that glues a node or resource with an edge (relationship).
Strong Typing#
Both Mount and ForeignMount have the same type signature:
edge_definition[, resource=ResourceType, array=True|False]
The declared field automatically converted to a proper ResourceEdge type. When setting array=True, the type becomes a list of ResourceEdge objects. Setting resource=... makes the type a resource instead of a node (e.g., ResourceEdge[CommentResource, Link]). If the provided
edge definition uses the model (edge = Relation(model=...)), the type of ResourceEdge will
use it instead of the default Link.
Note
The direction of the edge does not matter in the perspective of the resource. The type of the following mounts will be the same:
class User(Node):
wrote = Relation(Post)
written_by = Relation(Post, incoming=True)
class PostResource(Resource):
post = Pivot(Post)
author = Mount(Post.wrote) # type: ResourceEdge[User, Link]
written_by = Mount(Post.written_by) # type: ResourceEdge[User, Link]
What is ResourceEdge?#
A ResourceEdge is a small object that holds both the related node (or resource) and the edge (relationship) data. Its type signature is:
class ResourceEdge(Generic[NodeType, EdgeType]):
resource: NodeType # The related node or resource
edge: EdgeType | None # The edge data (can be None)
For example, ResourceEdge[User, Link] means you get a User node and the Link (relationship) between your pivot and that user.
If you use
resource=...in your mount,NodeTypebecomes your resource class instead of a node.If you use
array=True, you get a list ofResourceEdgeobjects.
Mount vs ForeignMount: What Declares the Edge?#
The key difference between Mount and ForeignMount is where the edge is declared in your data model, not the direction of the relationship.
Mount: Use when the edge is declared on the pivot node (the main node of your resource).
ForeignMount: Use when the edge is declared on another node, but you want to access related nodes from the perspective of your pivot.
Direction does not matter—the direction is set by the incoming=True flag in your model’s Relation or FutureRelation. Always check where the edge is actually declared in your node classes to decide which mount type to use.
Example:
class User(Node):
...
class Post(Node):
author = Relation(to=User)
class PostResource(Resource):
post = Pivot(Post)
author = Mount(Post.author) # Post declares the edge 'author'
class UserResource(Resource):
user = Pivot(User)
# Declared not by User, but by Post, so we use ForeignMount
posts = ForeignMount(Post.author, array=True)
In
PostResource, useMountbecausePostdeclares theauthoredge.In
UserResource, useForeignMountbecause you want to collect all posts that reference the user, but the edge is declared onPost.
Best Practices#
Always declare exactly one
Pivotper resource.Use
Mountwhen the edge is declared on the pivot node.Use
ForeignMountwhen the edge is declared on another node.Use
array=Trueto collect multiple edges.Use
resource=...to nest resources for deep subgraphs.
See the API docs for nova.resource.base.Resource, nova.resource.base.Mount, nova.resource.base.ForeignMount, and nova.resource.base.ResourceEdge for more details.