Problem
adapter-store consumers can only get a single resource into the store via retrieveById(id: number). For Laravel backends using custom route-model binding, the show route often accepts a string key as well — e.g. kendo's Issue::resolveRouteBinding resolves both numeric ids and issue keys, so GET projects/1/issues/KD-0123 and GET projects/1/issues/123 both work.
Because the store has no typed path for string keys, kendo's issue Show/Edit pages currently work around it by calling retrieveAll() and resolving the key client-side — fetching 600+ issues in production to display one, on every page mount (retrieveAll has no caching). We want to fix that, and the fix needs a sanctioned way to fetch one resource by key.
Constraints we want to keep:
setById stays internal — the public StoreModuleForAdapter surface remains read-only.
- No type-system abuse in consumers (
retrieveById(slug as unknown as number) works at runtime since the value is only URL-interpolated, but we don't want to ship that).
Options
Option 1 — first-class retrieveByKey(key: string)
A sibling of retrieveById, identical implementation:
retrieveByKey: async (key: string) => {
const { data } = await httpService.getRequest(`${domainName}/${key}`);
setById(data);
},
- Laravel route-model binding with custom keys is a first-class framework concept, so "fetch one resource by string key" arguably belongs in the package's domain rather than being app-specific.
setById stays internal, numeric call sites keep strict number typing.
- Misuse surface: calling it on a store whose backend only binds ids yields a 404 — the same failure mode as
retrieveById(999999) today.
- Narrow: solves exactly this need and nothing else; a future store-level need means another package change.
Option 2 — generic extend config hook
Capability injection in the style of the existing broadcast config: the hook runs once at store creation and receives the same internal module tier the adapter factory already gets.
type AdapterStoreConfig<T, E, N, X extends object = {}> = {
// ...existing...
broadcast?: AdapterStoreBroadcast<T>;
extend?: (storeModule: AdapterStoreModule<T>) => X;
};
// createAdapterStoreModule returns StoreModuleForAdapter<T, E, N> & X
Consumer side:
extend: ({setById}) => ({
retrieveBySlug: async (slug: string): Promise<void> => {
const {data} = await httpService.getRequest<IssueResource>(`${url}/${slug}`);
setById(data);
},
}),
- Most future-proof: consumers define their own sanctioned store-level methods without further package changes, and nothing app-specific (like "slug") enters the package.
- The trust boundary is unchanged in kind —
adapter and broadcast.subscribe already receive the internal tier at creation time; extend is the same pattern generalized.
- It does relax the current discipline that custom writes are adapter methods tied to a resource — store-level custom methods become a thing. Whether that's a direction the package wants is the main question here.
Option 3 — widen retrieveById to number | string
Smallest possible diff. Trade-off is semantic: a method named ById accepting keys blurs what actually binds, and every store starts accepting strings whether or not its backend supports them.
Option 4 — no package change (consumer-side resolve-then-retrieve)
The consumer does a plain getRequest on the show route to learn the numeric id, then calls the typed retrieveById(data.id):
- numeric value → 1 request (straight
retrieveById)
- string key → 2 requests, the second re-fetching a payload the consumer already had, because
retrieveById is the only sanctioned door into the store
Zero package work, fully typed, mildly wasteful by design. Also viable as an interim while one of the other options is discussed/released, since the consumer-side fix isn't blocked on a package release.
Ask
Which direction fits the package best? Happy to PR whichever option we agree on.
Context: found while fixing kendo's issue Show page perf (600-issue fetch to render one issue).
Problem
adapter-storeconsumers can only get a single resource into the store viaretrieveById(id: number). For Laravel backends using custom route-model binding, the show route often accepts a string key as well — e.g. kendo'sIssue::resolveRouteBindingresolves both numeric ids and issue keys, soGET projects/1/issues/KD-0123andGET projects/1/issues/123both work.Because the store has no typed path for string keys, kendo's issue Show/Edit pages currently work around it by calling
retrieveAll()and resolving the key client-side — fetching 600+ issues in production to display one, on every page mount (retrieveAllhas no caching). We want to fix that, and the fix needs a sanctioned way to fetch one resource by key.Constraints we want to keep:
setByIdstays internal — the publicStoreModuleForAdaptersurface remains read-only.retrieveById(slug as unknown as number)works at runtime since the value is only URL-interpolated, but we don't want to ship that).Options
Option 1 — first-class
retrieveByKey(key: string)A sibling of
retrieveById, identical implementation:setByIdstays internal, numeric call sites keep strictnumbertyping.retrieveById(999999)today.Option 2 — generic
extendconfig hookCapability injection in the style of the existing
broadcastconfig: the hook runs once at store creation and receives the same internal module tier theadapterfactory already gets.Consumer side:
adapterandbroadcast.subscribealready receive the internal tier at creation time;extendis the same pattern generalized.Option 3 — widen
retrieveByIdtonumber | stringSmallest possible diff. Trade-off is semantic: a method named
ByIdaccepting keys blurs what actually binds, and every store starts accepting strings whether or not its backend supports them.Option 4 — no package change (consumer-side resolve-then-retrieve)
The consumer does a plain
getRequeston the show route to learn the numeric id, then calls the typedretrieveById(data.id):retrieveById)retrieveByIdis the only sanctioned door into the storeZero package work, fully typed, mildly wasteful by design. Also viable as an interim while one of the other options is discussed/released, since the consumer-side fix isn't blocked on a package release.
Ask
Which direction fits the package best? Happy to PR whichever option we agree on.
Context: found while fixing kendo's issue Show page perf (600-issue fetch to render one issue).