Relations
Relations are the structural connectors between entities in your domain model. They define how data flows from one entity to another, and they directly determine how the mock runtime resolves nested fields in GraphQL queries. A well-defined set of relations produces predictable, nested API responses. A poorly configured set produces empty fields, incorrect shapes, or silent data loss.
This page covers every aspect of relation management: types, creation methods, direction and ownership semantics, cardinality consequences, and the causal chain from relation definition to runtime behavior.
Why Relations Matter
Every relation you define on the modelling board becomes a nested field in your GraphQL schema. When an API consumer queries a Customer and requests its orders, the runtime uses the relation definition to locate, join, and assemble the related Order records. Without a correctly configured relation, that nested field either returns an empty array or does not appear at all.
Relations are not decorative lines on a board. They are executable instructions for the query engine.
Relation Types
Mockomat supports three relation cardinalities. Each type determines the GraphQL return shape and the MongoDB lookup strategy the runtime uses at query time.
One-to-One (1:1)
A one-to-one relation connects exactly one record in the source entity to exactly one record in the target entity.
| Property | Value |
|---|---|
| Example | User -> Profile |
| GraphQL return type | Single object (or null) |
| Runtime behavior | Lookup returns at most one document |
Use 1:1 relations when an entity has a single, tightly coupled companion. Typical cases include profile records, configuration objects, or billing details that always exist in a one-to-one correspondence with the parent.


One-to-one relation between User and Profile on the modelling board.
One-to-Many (1:n)
A one-to-many relation connects one record in the source entity to multiple records in the target entity.
| Property | Value |
|---|---|
| Example | Customer -> Order[] |
| GraphQL return type | Array of objects |
| Runtime behavior | Lookup returns a collection of matching documents |
This is the most common relation type. It represents ownership hierarchies: a customer owns orders, a project contains tasks, a category groups products. The parent entity exposes the children as a list field.


One-to-many relation between Customer and Order with array return.
Many-to-Many (m:n)
A many-to-many relation connects multiple records on both sides. Each record in the source entity can relate to multiple records in the target entity, and vice versa.
| Property | Value |
|---|---|
| Example | Product <-> Category |
| GraphQL return type | Array on both sides |
| Runtime behavior | Implicit join resolves through shared references |
Many-to-many relations are appropriate when neither side exclusively owns the other. Products belong to multiple categories; categories contain multiple products. The runtime resolves these through an implicit join — no explicit junction table is required in your model.


Many-to-many relation between Product and Category.
Creating Relations
There are two ways to create a relation in Mockomat.
Drag-to-Connect on the Modelling Board
On the modelling board, each table card exposes connection points. To create a relation:
- Hover over a table card to reveal its connection handles.
- Click and drag from a handle on the source entity toward the target entity.
- Release over the target entity's connection area.
- A relation configuration dialog opens, where you specify cardinality, direction, and linking columns.
This method is fast and visually intuitive. It works well when you are designing the domain structure and want to see the spatial relationships between entities in real time.


Drag-to-connect interaction for creating a relation on the board.
Relation Editor in the Table Detail View
When you open a table in the detail view (by clicking on a table card or navigating from the tables and attributes configuration), a dedicated relation section allows you to create and manage relations:
- Click the add relation action in the relation section.
- Select the target entity from the entity list.
- Choose the cardinality (1:1, 1:n, or m:n).
- Define the direction — which entity is the parent and which is the child.
- Specify the linking columns that connect the two entities.
This method gives you precise control over every parameter and is preferable when you need to configure complex linking logic or review existing relations in detail.


Relation editor panel in the table detail view.
Direction and Ownership
Every relation has a direction. Direction determines which entity is the parent (the one that exposes the nested field) and which is the child (the one that appears as a nested object or array in the parent's response).
Parent and Child Semantics
- The parent entity owns the relation and exposes the child as a queryable nested field.
- The child entity is the target of the lookup. Its records are assembled and attached to the parent during query resolution.
For example, in a Customer -> Order[] relation, Customer is the parent. When you query customers, each customer record can include its related orders as a nested array. The direction establishes this nesting hierarchy.
Ownership vs. Reference
Not all relations represent ownership. Consider these two cases:
| Relation | Pattern | Meaning |
|---|---|---|
Customer -> Order[] | Ownership | A customer owns their orders. Deleting the customer conceptually orphans the orders. |
Order -> PaymentMethod | Reference | An order references a payment method. The payment method exists independently. |
Both are valid relations, but they carry different semantic weight. Ownership relations typically flow from aggregate root to dependent entity. Reference relations connect independent entities that happen to interact. Being explicit about this distinction improves the readability of your model and prevents confusion when reviewing nested query behavior.
Bidirectional Access
Depending on configuration, both entities in a relation may expose the counterpart as a nested field. In a Customer -> Order[] relation, the customer exposes orders. But the order can also expose its parent customer, enabling queries like:
query {
orders(limit: 10) {
id
totalAmount
customer {
id
name
}
}
}Bidirectional access is controlled through the relation configuration. By default, the parent exposes the child. Reverse access must be explicitly enabled.


Direction and bidirectional access settings in the relation editor.
Cardinality Consequences
Cardinality is not a cosmetic label. It directly controls the GraphQL return type and the runtime lookup strategy.
| Cardinality | GraphQL Field Type | Lookup Strategy |
|---|---|---|
| 1:1 | TargetEntity (single object or null) | Match at most one document |
| 1:n | [TargetEntity] (array) | Match all documents with the parent reference |
| m:n | [TargetEntity] (array on both sides) | Implicit join through shared references |
Incorrect Cardinality
Setting the wrong cardinality leads to unexpected response shapes. For example:
- A relation marked as 1:1 when the data is actually 1:n causes the runtime to return only the first matching record, silently discarding the rest.
- A relation marked as 1:n when the data is actually 1:1 returns an array containing a single element, forcing consumers to unwrap an unnecessary list.
Review your cardinality choices carefully. If you are unsure whether a relation is 1:n or m:n, consider the future state of the domain, not just the current data shape.
Quality Checklist
Before finalizing any relation, verify the following:
- Ownership or reference? Does this relation represent real ownership (a customer owns their orders) or a cross-reference (an order references a payment method)?
- Direction clarity. Is it immediately obvious which entity is the parent and which is the child? Would a new team member understand the nesting hierarchy?
- Lookup behavior. When querying the parent, should child data be included by default? Should reverse lookups be available?
- Cardinality correctness. Is this truly 1:n, or could it evolve into m:n as the domain matures?
- Linking column validity. Do the specified linking columns exist and contain matching values in the underlying data?
Addressing these questions before moving to preview and hints validation prevents the most common relation-related issues.
Causality: Relations, Runtime Lookups, and Nested Queries
Understanding the causal chain from relation definition to query result is essential for diagnosing issues and building reliable mock APIs.
Stage 1: Relation Definition
You define a relation on the modelling board or in the table detail view. The definition includes source entity, target entity, cardinality, direction, and linking columns. This definition is stored as metadata alongside your project model.
Stage 2: Schema Generation
When the runtime activates, each relation becomes a nested field in the generated GraphQL schema. A Customer -> Order[] relation produces an orders field on the Customer type with return type [Order].
Stage 3: Query Resolution
When a consumer sends a query that includes a nested field, the runtime executes the following pipeline:
- Query planner identifies the nested field and locates the corresponding relation definition.
- MongoDB query builder constructs a
$lookupaggregation stage using the linking columns from the relation. - Result assembler attaches the lookup results to the parent documents in the correct shape (object for 1:1, array for 1:n and m:n).
Stage 4: Response Delivery
The assembled response is returned to the consumer as a standard GraphQL response. The nested structure mirrors the relation definitions exactly.


Causal flow from relation definition through runtime pipeline to nested query response.
Common Failure Modes
| Symptom | Likely Cause | Resolution |
|---|---|---|
| Nested field returns empty array | Wrong direction or missing linking column | Verify relation direction and confirm linking columns contain matching values |
Nested field returns null on 1:1 | Target record does not exist for the given reference | Check data integrity or adjust cardinality to optional |
| Unexpected single object instead of array | Cardinality set to 1:1 instead of 1:n | Update cardinality in the relation editor |
| Deeply nested query times out | Excessive relation depth (e.g., 4+ levels) | Flatten the query or reduce nesting depth |
Relation Depth
Each level of nesting in a GraphQL query traverses one relation and triggers one additional MongoDB lookup stage. Deeply nested queries (three or more levels) increase query complexity and execution time. Design your relations with query depth in mind — prefer flatter structures where possible, and use the preview and hints workflow to validate response times before exposing endpoints to consumers.
Summary
Relations are the mechanism that transforms a flat collection of entities into a connected domain model capable of serving nested GraphQL queries. Each relation carries precise semantics — type, direction, cardinality, and linking columns — that the runtime translates into MongoDB lookup operations. Correct relation configuration produces predictable, well-shaped API responses. Misconfigured relations produce silent data gaps that are difficult to diagnose without understanding the causal chain.
Use the modelling board for visual relation design, the tables and attributes detail view for precise configuration, the preview and hints workflow for validation, and the API design view to control how relation-derived fields are exposed to consumers.