Skip to main content
Wildo.ai Coming soon

Declaring a resource

Give every relationship a clear meaning

Describe ownership and references so connected records behave according to the relationship they actually have.

A task belongs inside a project and separately refers to Alex as its assignee.

Give every relationship a clear meaning

A task belongs to a list and can be assigned to a person. Those connections mean different things: the list organizes the work it owns, while the person exists independently of any task.

Wildo makes that meaning part of the relationship declaration. You describe how many records can be connected, which field holds the reference and how the connection participates in the application.

That shared model supports addressing, related information and checks on the records being linked. You keep control of what ownership means and which connections the business permits.

Example — Assign someone from the right team

A task can refer to a team member as its assignee. Being allowed to assign the task and being eligible to receive it are different questions: the application can require that the chosen person belongs to the task’s organization.

For engineers

Pair the field with an explicit edge

A foreign-key field identifies the stored value. The module’s relationship declaration explains what that value connects to. Cardinality states how many records can participate, while nature distinguishes composition, reference and association semantics.

Wonder Todos stores these fields on Todos_BaseSchema in todos.schemas.ts. They are selected fields from the object schema, not a complete resource definition:

todoListId: z.string().min(1).isDBIndexed().isForeignKey().isSummaryField().excludeFromUpdate(),
assignedToUserId: z.string().isDBIndexed().isForeignKey().optional(),

todoListId is required and excluded from ordinary updates. assignedToUserId is optional. Naming both as foreign keys is only half of the declaration: the module graph must identify the corresponding resource and meaning for each exact field name.

Declare what owns the record

The list-to-task edge is a composition. This is the actual declaration from the same module, with its source comment omitted:

createResourcesRelationship(
  TasksManager_ResourceType.TODO_LISTS, TasksManager_ResourceType.TODOS,
  ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY,
  {
    nature: RelationshipNature.COMPOSITION,
    foreignKeyField: 'todoListId',
    accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.OPTIONAL_CONTEXT,
    contextPolicy: {
      operationOverrides: {
        [CoreResourceOperation.LIST]: { enabled: false },
        [CoreResourceOperation.SEARCH]: { enabled: false },
      },
    },
  },
),

One list owns many tasks, with todoListId stored on the task. OPTIONAL_CONTEXT concerns addressing; it does not make that required schema field optional. The edge disables context expansion for list and search operations. It does not declare an onParentDelete cascade: configure child lifecycle explicitly when deletion must act on children.

Reference something that exists independently

This assignee relationship comes from Wonder Todos’ tasks-manager.relationships.ts. Standalone source comments are omitted; the declaration itself is retained:

createResourcesRelationship(
  TasksManager_ResourceType.TODOS, CoreResourceType.USERS,
  ResourceRelationshipCardinality.MANY, ResourceRelationshipCardinality.ONE,
  {
    nature: RelationshipNature.REFERENCE,
    foreignKeyField: 'assignedToUserId', // Explicit for Edge Case #1 - multiple FKs to same type
    parentResourceRequirement: ResourceParentResourceRequirement.OPTIONAL,
    accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.OPTIONAL_CONTEXT,
    scopeMembership: true,
    contextPolicy: {
      objectMode: ContextPolicy_ObjectMode.SUMMARY,
      operationOverrides: {
        [CoreResourceOperation.READ]: { objectMode: ContextPolicy_ObjectMode.FULL },
        [CoreResourceOperation.LIST]: { enabled: false },
        [CoreResourceOperation.SEARCH]: { enabled: false },
      }
    }
  }
),

Many tasks can reference one user through assignedToUserId. The parent requirement is optional, so an unassigned task is valid. scopeMembership requires the target to belong to the relevant scope; it handles the fact that user identity and organization membership are different records.

Choose the relationship by its meaning

Business connectionDeclarationWhat stays distinct
A list owns its tasksComposition through todoListIdOwnership permits lifecycle policies; it does not select an automatic cascade.
A task points to its assigneeReference through assignedToUserIdThe person is independently owned. Target membership is checked separately from the caller’s permission to assign.
People belong to organizations through membershipsMany-to-many association through ORGANIZATION_MEMBERSRoles and membership state belong on the junction record, not on the person or organization.

The engine’s organization/user association declares jointResourceType: CoreResourceType.ORGANIZATION_MEMBERS. See connections with their own information for the complete association entry and the distinct setup for links between two tasks.

accessScopeStrategy controls whether the parent participates in an address. contextPolicy controls which related data the runtime assembles. Here the default user context is a summary, the read operation requests the full object, and list and search disable that expansion.

These choices avoid treating every relationship as a request to fetch a complete related record on every screen. Choose the detail needed by each operation and the corresponding interface.

Put business eligibility in the right place

The relationship supplies the shared structure and membership requirement. An operation can add referenceConstraints when a particular action requires a narrower target, such as an active member with a certain role. The permission to run the operation remains its own access decision.

The write-integrity path checks referenced records through the assembled relationship model. Keep schema foreign keys, module edges and specifications aligned when adding or changing a connection. For composition, declare the intended child lifecycle explicitly rather than assuming the word ownership selects a deletion policy.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.