Datasource
Datasource is the contract between Monster controls and data. Use it when plain demo state is no longer enough and you need stable read, write and state handling without pushing fetch logic into every component.
Understand where transport logic belongs and how UI components should consume it
By the end of this tutorial you will know when to start with the base Datasource, when to switch to server-backed variants and how controls such as datatables and save buttons use the same contract.
Before You Start
Know how examples and runtime files are loaded
Datasource is not the first Monster topic. If imports, example files or the Playground still feel unclear, use First Page before this page.
Know the boundary
A datasource owns loading, writing and state transitions. Components should own presentation, option handling and user interaction, not raw transport code.
Think in public state, not ad-hoc fetches
Once multiple controls depend on the same records, read/write status and error handling need one shared contract. Datasource is that contract.
The Minimal Flow
1. Start from a datasource, not from a widget callback
The base class gives you one place to encapsulate records, selection, loading status and persistence rules.
import { Datasource } from "@schukai/monster/source/data/datasource.mjs";2. Put record shape and behavior into one class
That keeps the transport contract explicit and stops UI code from guessing paths or payload shapes in random places.
class ProductDatasource extends Datasource {
get defaults() {
return Object.assign({}, super.defaults, {
records: [],
state: {
loading: false,
dirty: false,
},
});
}
}3. Use a server-backed datasource when transport matters
As soon as data comes from an API, move to the server datasource family rather than bolting fetch calls onto every control.
import { Server } from "@schukai/monster/source/data/datasource/server.mjs";
class ProductServerDatasource extends Server {
get defaults() {
return Object.assign({}, super.defaults, {
read: {
url: "/api/products",
path: "items",
},
write: {
url: "/api/products",
},
});
}
}4. Let controls consume the datasource through public hooks
Components such as datatables, save buttons or selectors should point to the datasource, not duplicate its read/write responsibilities.
<monster-datasource-rest
id="products-rest"
data-monster-option-features-autoinit="true"
data-monster-option-read-url="/api/products"
data-monster-option-read-path="items">
</monster-datasource-rest>
<monster-save-button
data-monster-option-datasource-selector="#products-rest">
</monster-save-button>Important mental model
Datasource is not just a fetch helper. It is the state boundary between UI and transport. Once a component bypasses that boundary, status handling, dirty tracking and persistence rules start to fragment.
Core Concepts
| Concept | Use it for | Typical mistake |
|---|---|---|
records | The current data set a control should render or manipulate. | Letting every control maintain its own copy of the same records. |
read | Loading records from a remote or local source in one declarative place. | Triggering ad-hoc network requests from click handlers and render logic. |
write | Persisting changed records back to the backend through a stable contract. | Posting from unrelated components that do not own the data policy. |
state | Representing loading, dirty and error conditions for connected controls. | Treating async state as hidden local flags that other controls cannot see. |
selector binding | Connecting controls to an existing datasource element declaratively. | Passing copied snapshots instead of sharing one datasource instance. |
Where Datasource Fits Best
The contract becomes most valuable as soon as more than one control cares about the same data and status. That is where shared state and persistence discipline matter.
Datatables and filters
One datasource coordinates records, pagination, filters and persistence.
Save and change indicators
Buttons can reflect dirty or persisted state without guessing where the data came from.
Remote option sources
Select-like controls can consume normalized records instead of embedding fetch code inside the control.
Cross-control error handling
Notify or monitor components can react to one datasource error surface instead of many scattered try/catch blocks.
Common Mistakes
Putting fetch logic into the component
If the control owns HTTP details directly, reuse and testability drop immediately.
Splitting one dataset across unrelated stores
Dirty state, paging and writes become incoherent when each widget carries its own partial copy.
Using datasource as a generic dumping ground
Keep UI formatting and transport concerns separate. Datasource should own data contracts, not view rendering details.