
Show records from their original system
Some information already has a home: product prices in an ERP, service definitions in an accounting system or a catalogue maintained by another team. Your application may need to show those records without taking responsibility for a second copy.
A virtual resource reads from the external system when the application asks for it. Wildo maps the remote fields into the resource’s declared shape, so the information can participate in the application’s read operations and views.
The source continues to own changes. This is a useful fit for information people need to consult, especially when keeping a local copy would create another synchronization job.
Example — Consult the company’s service catalogue
Wonder Todos reads billable services from the company’s Odoo catalogue. A team member can see the service name, price and unit through the application, while the finance team continues to maintain those values in Odoo.
For engineers
Declare the source and its field mapping
The following excerpt from Wonder Todos’ billable-services.resources-config.ts selects the HTTP API adapter and binds it to Odoo. Source comments are omitted; the resource’s operation configurations follow this excerpt:
persistenceAdapter: PersistenceAdapter.HTTP_API,
httpApiBinding: {
dialect: HttpApiTransportDialect.ODOO_JSONRPC,
providerRef: 'odoo',
entityRef: 'product.template',
keyFields: [{ localField: 'odooProductId', remoteField: 'id', codec: HttpApiKeyComponentCodec.INTEGER }],
fieldMappings: [
{ localField: 'listPrice', remoteField: 'list_price' },
{ localField: 'unitOfMeasure', remoteField: 'uom_name' },
],
tenancy: {
stance: HttpApiTenancyStance.SINGLE_TENANT_BINDING,
justification: 'Wonder Todos serves one company, whose Odoo holds one service catalogue; there is no per-tenant partition to push down.',
},
erasure: { stance: HttpApiErasureStance.NO_SUBJECT_DATA },
},
coreOperations: [
CoreResourceOperation.READ,
CoreResourceOperation.LIST,
],
The binding names a provider, a transport dialect and the remote entity. keyFields defines how a remote row is identified. fieldMappings translates names where the application and remote system differ: listPrice reads list_price, while fields with matching names can retain them.
Declare only the queries the remote dialect can execute
The same Odoo resource’s LIST variant declares:
sortFields: ['name', 'listPrice', 'odooProductId'],
listPrice maps to the remote list_price field. A display value without an orderable remote counterpart should not be advertised as sortable.
| Dialect | LIST/SEARCH sort declaration |
|---|---|
ODOO_JSONRPC or ODATA | Declare a nonempty set of sortable fields; startup refuses an absent declaration. |
REST_JSON | Do not declare sort fields: this adapter path does not translate them. |
The application also declares how callers address the resource. Wonder Todos’ relationship uses a standalone reference to its external catalogue:
createResourcesRelationship(
CoreResourceType.ORGANIZATIONS, TasksManager_ResourceType.BILLABLE_SERVICES,
ResourceRelationshipCardinality.ONE, ResourceRelationshipCardinality.MANY,
{
nature: RelationshipNature.REFERENCE,
isPrimaryScope: true,
accessScopeStrategy: ResourceRelationshipAccessScopeStrategy.STANDALONE,
contextPolicy: { enabled: false },
}
),
This relationship establishes the access scope and addressing strategy; the READ and LIST operation roles grant organization members access. It does not invent an organization column in Odoo or filter remote rows by a local ownership relationship. Remote partitioning remains the binding’s tenancy responsibility.
Keep source access and application access connected
providerRef resolves the configured provider access rather than embedding credentials in the resource. The repository uses the shared HttpApiReadClient for transport and dialect-specific request construction. An ordinary authorized resource read becomes the corresponding remote request, whose result is projected into the local resource shape.
The application still declares READ/LIST roles and query choices. The binding separately declares tenancy: this example serves one company’s catalogue to the application’s organizations. A multi-company integration needs a tenancy declaration that matches the remote partitioning. The erasure stance describes the bound data and is a separate responsibility from transport configuration.
Choose virtual data when consultation is the goal
A virtual resource has no local record to enrich with extra columns, attachments or a retrieval index. Requests depend on the external system’s response time and availability, and the source’s dialect determines which queries can be translated.
For this catalogue, those properties fit the product: prices are maintained elsewhere and the application reads them. When people need local annotations, attached files or ordinary local relationships around imported records, use a copy-in pipeline instead.
The read-through resource and the pipeline share remote-request machinery, while keeping their different storage and ownership choices explicit.