Appearance
Rest Resource Source Reading Map
This page is a practical map for contributors and AI workflows that need to read the rest-resource module efficiently.
Use this page when the main question is not only what ModelResource does in isolation, but how the whole module works at runtime:
- which files form the module entry surface?
- how do routes reach page controllers?
- why are the page controllers so thin?
- where do list-page and entry-page blocks actually come from?
- where does the real query, form, and mutation ownership live?
Use this page together with:
- Model Resource Owner Pattern
- Rest Resource Under the Hood
- Using
ModelResourcein Your Module - Table + Resource CRUD Cookbook
- Form Guide
- Zova Source Reading Map
Use this page after Rest Resource Under the Hood when your next question is not the runtime model itself, but which files to read first for one specific rest-resource question.
TIP
Rest Resource docs paths
- Architecture path: Model Resource Owner Pattern → Rest Resource Under the Hood → Rest Resource Source Reading Map
- Application path: Model Resource Owner Pattern → Using
ModelResourcein Your Module → Resource Model Cookbook
You are here: module-level source reading. Previous page: Rest Resource Under the Hood.
Why this page exists
The existing ModelResource pages already explain two important things well:
- why
ModelResourceis a resource-owner model - how application code should reuse it directly or through a thin facade
What those pages do not focus on is the module-level runtime path around that model:
- route records
- generated page wrappers
- page-shell controllers
- block rendering from schema metadata
- downstream generic consumers such as
basic-pageandbasic-pageentry
That missing layer matters because rest-resource is not only a reusable model bean.
It is also a route-to-page-shell-to-block-to-model bridge for resource-driven CRUD pages.
The shortest accurate runtime model
If you only remember one mental model, remember this one:
routes.tsdeclares three ordinary resource workspace routes and one host-dependent picker route- generated
ZPage*wrappers bind those route entries to page controllers - the ordinary page controllers resolve the current
resource,id, andformScene, while the picker controller also requires its host-scoped picker contract - the page controllers load top-level schema metadata and render
rest.blocks - those rendered blocks usually enter generic Basic runtimes such as
basic-page:blockPageorbasic-pageentry:blockPageEntry - those downstream runtimes resolve the same selector-backed
ModelResourceinstance again ModelResourceremains the stable owner of resource bootstrap, schema, permissions, queries, mutations, and invalidation
A compact relationship map is:
text
routes.ts
└─ ZPageResource / ZPageEntry / ZPageEntryCreate / ZPageResourcePicker
└─ page controller shell
└─ schema rest.blocks
└─ generic Basic blocks
└─ selector-backed ModelResource
└─ OpenAPI bootstrap / fetch / mutation / invalidationThe picker route is intentionally different from an ordinary workspace page: it consumes an injected host contract and is normally entered through a routed dialog. For its public authoring contract, see Resource Picker Guide.
That is why rest-resource should not be read as “one page that does CRUD directly”.
The Zova-native meaning is:
rest-resourceprovides the module-level bridgeModelResourceprovides the resource-owner boundarybasic-pageandbasic-pageentryprovide the deeper list/form runtime
The core source-reading path
When you want the shortest correct reading order, use this sequence:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/routes.tszova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/page/resource.tszova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/page/entry.tszova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/page/entryCreate.tszova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/page/resourcePicker.tszova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entryCreate/controller.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resourcePicker/controller.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.tszova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsxzova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsxzova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.delete.tsxzova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/index.ts
A compact role map is:
routes.tsshows the public route surface.metadata/page/*.tsshows the generated wrapper entry intocreateZovaComponentPage(...)page/resource/controller.tsxshows the list-page shellpage/entry/controller.tsxshows the entry-page shellpage/entryCreate/controller.tsxshows virtual create-page reusepage/resourcePicker/controller.tsxshows the host-dependent picker shellmodel/resource.tsshows the resource-owner corebasic-page:blockPageshows the deeper list runtimebasic-pageentry:blockPageEntryshows the deeper form runtimecommand.delete.tsxshows non-page consumers reusing the same resource owner.metadata/index.tsshows the generated typing and bean-registration surface
1. Route surface and tab identity
Start with:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/routes.ts
This file declares four routes:
:resource:resource/create:resource/:id/:formScene?:resource/picker
The first three are ordinary resource workspace routes. The fourth is the host-dependent Resource Picker route; it is normally rendered by a routed-dialog workflow, not entered as a direct browser destination. See Resource Picker Guide for the public authoring contract.
The module is not tied to one concrete resource such as Student or Product. Its runtime identity comes from route.params.resource.
Why tabKey matters
The list, create, and entry routes use the same tabKey(route) shape:
typescript
`/rest/resource/${encodeURIComponent(route.params.resource)}`;This means their workspace identity is resource-level rather than row-level. The picker route deliberately does not use that shared workspace identity: its controller requires the host-scoped picker contract that its routed-dialog caller provides.
If your next question becomes specifically how the entry route becomes a working page through basic-pageentry, continue with Resource Entry Page Deep Dive.
The ordinary workspace views are grouped under one resource-oriented tab boundary:
- resource list
- create entry
- view/edit entry
That is a small but important architectural clue.
If your next question is specifically how filter state becomes query, then ModelResource.select(query), and finally list/paged data, continue with Filter to Query to Select Data Flow Guide.
If your next question is specifically how the resource owner itself works internally, continue with ModelResource Internals Deep Dive.
The module is designed around one stable resource workspace, not one isolated page per row.
2. Generated wrapper layer and typed page identity
Read next:
src/.metadata/page/resource.tssrc/.metadata/page/entry.tssrc/.metadata/page/entryCreate.ts
These files are thin but important.
They show that the route component is not hand-written Vue setup code.
Instead, each route enters the normal Zova page-controller path through:
createZovaComponentPage(ControllerPageResource, ...)createZovaComponentPage(ControllerPageEntry, ...)createZovaComponentPage(ControllerPageEntryCreate, ...)createZovaComponentPage(ControllerPageResourcePicker, ...)
These files also expose the typed Zod-based params schema for each page.
What .metadata/index.ts adds
After the page wrappers, read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/.metadata/index.ts
This generated file is mostly not “business logic”, but it is still the best summary of the module’s registered surfaces:
rest-resource.model.resource→ model bean full namerest-resource.controller.pageResource/pageEntry/pageEntryCreate/pageResourcePicker→ controller registrations- page-path and page-name typing for
/rest/resource/..., including the hosted picker route - module scope typing for
rest-resource
Use this file as the registry map, not as the first place to learn the runtime behavior.
3. Thin page-shell controllers
The most important module-level reading insight is this:
the page controllers are thin shells that load top-level schema metadata and render blocks; they are not the whole CRUD runtime by themselves.
That becomes clear when you read the two real page controllers.
3.1 Resource list shell
Read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resource/controller.tsx
This controller does three main things:
- resolves the selector-backed
ModelResourceinstance fromthis.$params.resource - ensures
apiSchemasSelect.sdkis loaded - reads
schemaRow?.rest?.blocksand renders those blocks throughZovaJsx
The key point is what it does not do.
It does not own:
- filter state
- pager state
- table refresh logic
- row data fetch orchestration
Those deeper concerns usually appear later inside the rendered block chain, especially in basic-page:blockPage.
So this controller is better understood as a resource list page shell.
It resolves resource context, loads the schema surface, and lets schema metadata choose which blocks should render.
3.2 Entry shell
Read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entry/controller.tsx
This controller is the entry-page counterpart.
It mainly owns:
- parameter interpretation (
resource,id,formScene) formMetaderivationformProviderlookup fromModelResourceformSchemalookup fromModelResource- ensuring the form API schemas are loaded
- block rendering from
formSchema?.rest?.blocks
Again, the most important insight is what it does not fully own.
It does not directly become the full form runtime.
The actual form-state, form-data, submit, and page-meta orchestration commonly appear deeper in downstream consumers such as basic-pageentry:blockPageEntry.
So this controller is a schema-driven entry shell, not a page-local CRUD implementation.
3.3 Virtual create shell reuse
Read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/entryCreate/controller.tsx
This file is intentionally small.
It is a virtual subclass of ControllerPageEntry.
That tells you the module does not want two separate create/runtime implementations.
Instead, the create route reuses the entry shell and lets route params plus form-scene logic select the create behavior.
This is a good example of Zova keeping page identity separate from duplicated controller logic.
3.4 Resource Picker shell is host-dependent
Read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/page/resourcePicker/controller.tsx
This controller resolves the selector-backed target resource and its select schema, then renders the schema's list blocks through a picker page host. Unlike the ordinary list and entry shells, it requires an injected host contract whose resource must match the route parameter. The host adapts the resource's existing list blocks for selection and supplies the confirm/cancel workflow.
That makes the picker route an internal routed-dialog workflow page, not a direct browser-entry CRUD page. For the supported field metadata and result contract, continue with Resource Picker Guide.
4. ModelResource is the owner core, not only a helper
Read:
zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts
This file is the real owner boundary of the module.
The earlier page Model Resource Owner Pattern already covers this class in depth, so here the focus is narrower:
- how it fits into the module-level runtime around it
- which methods the page-shell and downstream blocks actually depend on
What this model owns for the module
At module level, ModelResource provides the stable surface for:
- selector identity by resource name
- bootstrap through
$sdk.getBootstrap(this.resource) - resolution of
resourceApi - permissions lookup
- schema access for select/view/create/update
- form-provider access
- list/item query ownership
- create/update/delete mutation ownership
- invalidation policy
- form-facing helpers
A practical source-reading takeaway is:
the routes and page shells give the module its shape, but
ModelResourcegives the module its resource truth.
Why selector identity is still essential here
Because the module routes are generic, the same page/controller classes must work for many resources.
That only works cleanly because ModelResource uses selector identity.
The selector is the current resource name, so the same generic model class can safely serve:
- one Student resource instance
- one Product resource instance
- one Article resource instance
without cache collisions.
5. Downstream generic consumers are the next runtime layer
To understand the full runtime, do not stop at rest-resource itself.
Continue into the main downstream consumers.
5.1 basic-page:blockPage
Read:
zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx
This file shows where the deeper list-page runtime really lives:
- selector-based
ModelResourcelookup - query filter state
- paged query state
- list fetch through
select(this.query) - permission-sensitive table-meta refresh
- page-scene JSX/CEL scope
- rendering of filter/table/pager blocks
This is the strongest confirmation that ControllerPageResource is only the shell, while basic-page:blockPage is the richer list runtime.
5.2 basic-pageentry:blockPageEntry
Read:
zova/src/suite/cabloy-basic/modules/basic-pageentry/src/component/blockPageEntry/controller.tsx
This file shows the deeper entry runtime:
- selector-based
ModelResourcelookup formMeta,formProvider,formSchema, andformData- view-query loading for existing rows
- form submit delegation through
getFormMutationSubmit(...) - page-title/page-dirty updates
- form-scene JSX/CEL scope
This is the strongest confirmation that ControllerPageEntry is the page shell, while basic-pageentry:blockPageEntry is the richer form runtime.
5.3 Commands can reuse the same owner boundary
Read:
zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.delete.tsx
This file shows an important architectural property:
ModelResource is not only for page controllers.
A command bean can resolve the same selector-backed model and reuse its delete mutation directly.
So the owner boundary is reusable across:
- pages
- blocks
- commands
- custom business facades
6. What is runtime logic and what is generated scaffolding?
When reading this module, keep this distinction clear.
Runtime logic
These are the main runtime logic files:
src/routes.tssrc/page/resource/controller.tsxsrc/page/entry/controller.tsxsrc/page/entryCreate/controller.tsxsrc/page/resourcePicker/controller.tsxsrc/model/resource.ts
These are where the real behavior decisions live.
Generated or type-oriented scaffolding
These are mostly generated/type-facing support files:
src/.metadata/index.tssrc/.metadata/page/*.tssrc/types/resource.tssrc/types/pageWrapper.tssrc/types/pageEntryWrapper.ts
They are still useful, but for a different purpose:
- bean registration lookup
- page wrapper identity
- typed route params
- render-context augmentation
- bootstrap shape typing
Read them to confirm the public/typed surfaces, not to discover the main runtime flow first.
7. Question-oriented reading paths
Use these shorter paths when you have one specific question.
Question: how does one generic resource route become one page controller?
Read:
src/routes.tssrc/.metadata/page/resource.tsorsrc/.metadata/page/entry.ts- the corresponding
src/page/*/controller.tsx
Question: where does resource list-page block composition come from?
Read:
src/page/resource/controller.tsxsrc/model/resource.tsforschemaRowbasic-page/src/component/blockPage/controller.tsx
The important answer is usually:
- page shell reads
schemaRow.rest.blocks - generic Basic block runtime owns the deeper list behavior
Question: where does entry create/view/edit behavior come from?
Read:
src/page/entry/controller.tsxsrc/page/entryCreate/controller.tsxsrc/model/resource.tsforgetFormSchema,getFormApiSchemas,getFormData, andgetFormMutationSubmitbasic-pageentry/src/component/blockPageEntry/controller.tsx
Question: where do actual list query and form submit rules live?
Read:
src/model/resource.tsbasic-page:blockPagebasic-pageentry:blockPageEntry
This is the path that most clearly shows the separation between page shells and deeper generic runtimes.
Question: how do resource actions such as delete reach the same owner boundary?
Read:
basic-commands/src/bean/command.delete.tsxsrc/model/resource.ts
8. The Zova-native explanation
The most accurate Zova-native description is:
rest-resourceis a generic resource page module- the page controllers are schema-driven shells
ModelResourceis the selector-backed resource owner- generic Basic blocks are the deeper list/form runtime consumers
An approximate Vue-style translation would be:
- the module is not “one big page component with local hooks”
- it is closer to a route-driven controller shell that delegates into reusable runtime layers
That translation can help orientation, but the Zova meaning above is the authoritative one.
9. What to read next after this page
Choose the next page by the question you actually have.
If the next question is about the owner model itself
Continue with:
If the next question is about how to use the owner in your own business module
Continue with:
- Using
ModelResourcein Your Module - Resource Model Cookbook
- Resource Model Best Practices and Anti-Patterns
If the next question is about resource list-page blocks and tables
Continue with:
If the next question is about resource entry pages and forms
Continue with:
Final takeaway
The fastest accurate way to read rest-resource is to stop treating it as only one model file.
Read it as four cooperating layers:
- route surface
- generated page wrappers
- thin page-shell controllers
- selector-backed resource owner with downstream Basic block consumers
Once you see those layers clearly, the module becomes much easier to extend, debug, and explain without flattening Zova back into ad hoc page-local CRUD code.