@web-ts-toolkit/access-router-react
React hooks for @web-ts-toolkit/access-router-client model services.
This package provides a createModelHooks factory that binds one ModelService to eight hooks covering query and mutation flows.
Installation
- npm
- Yarn
- pnpm
- Bun
npm install react @web-ts-toolkit/access-router-react @web-ts-toolkit/access-router-client
yarn add react @web-ts-toolkit/access-router-react @web-ts-toolkit/access-router-client
pnpm add react @web-ts-toolkit/access-router-react @web-ts-toolkit/access-router-client
bun add react @web-ts-toolkit/access-router-react @web-ts-toolkit/access-router-client
Peer dependencies: react ^18 || ^19 and @web-ts-toolkit/access-router-client.
Quick Start
import { createModelHooks } from '@web-ts-toolkit/access-router-react';
import { adapter } from './api';
const organizationService = adapter.createModelService<Organization>({
modelName: 'Organization',
basePath: 'organizations',
});
const { useList, useRead, useCreate, useUpdate, useDelete } = createModelHooks({
modelService: organizationService,
});
function OrganizationList() {
const { data, isLoading, error } = useList({
listParams: { pageSize: 20 },
});
if (isLoading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<ul>
{data.map((org) => (
<li key={org._id}>{org.name}</li>
))}
</ul>
);
}
Factory
createModelHooks({ modelService })
Call the factory once, outside your components, and reuse the returned hooks.
const { useRead, useList, useCreate, useUpdate, useUpsert, useDelete, useCount, useDistinct } = createModelHooks({
modelService: organizationService,
});
The factory accepts one property:
| Property | Type | Description |
|---|---|---|
modelService | ModelService<T> | A model service created by adapter.createModelService(...) |
Naming Convention
The current API uses:
- verb-based hook names:
useRead,useList,useCreate,useUpdate,useUpsert,useDelete,useCount,useDistinct query(...)for query hooksmutate(...)for mutation hooks
That keeps the React surface consistent even though the underlying client methods still map to read, list, create, update, upsert, delete, count, and distinct operations.
Query Hooks
useRead
Fetches one model by ID.
const { data, isLoading, isFetching, error, query, refetch, reset } = useRead({
id: 'org_123',
advanced: true,
select: ['name', 'status'],
});
Important options:
idcontrols auto-fetchingadvanced: trueswitches toreadAdvanced(...)select,populate,sort,include, andtasksare forwarded to advanced readsbasicOptions,advancedOptions,enabled,initialData,requestConfig,onSuccess,onError, andonSettledcontrol request behavior
useList
Fetches a list of models.
const { data, totalCount, previousData, isLoading, isFetching, error, query, refetch, reset } = useList({
listParams: { pageSize: 20 },
filter: { status: 'active' },
advanced: true,
sort: { name: 1 },
keepPreviousData: true,
});
Important options:
listParamsdrives basic list requestsfilteris used for advanced listskeepPreviousDatapreserves the last resolved list during refetchsort,select,populate,include,tasks,basicOptions, andadvancedOptionsmap directly to client service arguments
useCount
Fetches a count.
const { data, isLoading, error, query, refetch, reset } = useCount({
advanced: true,
filter: { status: 'active' },
});
Use advanced: true when you need a filtered count.
useDistinct
Fetches distinct values for a field.
const { data, isLoading, error, query, refetch, reset } = useDistinct({
field: 'status',
conditions: { organizationId: 'org_123' },
});
If conditions is empty, the hook falls back to the basic distinct(...) route.
Mutation Hooks
useCreate
Creates a document.
const { data, isPending, error, mutate, reset } = useCreate({
advanced: true,
select: ['_id', 'name'],
});
await mutate({ name: 'Northwind Labs' });
useUpdate
Updates a document by ID.
const { data, isPending, error, mutate } = useUpdate();
await mutate('org_123', { status: 'active' });
useUpsert
Creates or updates, depending on the server-side upsert result.
const { data, isPending, error, mutate } = useUpsert();
await mutate({ _id: 'org_123', name: 'Northwind Labs' });
useDelete
Deletes a document by ID.
const { isPending, error, mutate } = useDelete();
await mutate('org_123');
Shared mutation behavior:
advanced: trueswitches to the corresponding advanced client method when availablemutate(...)performs the requestisPendingtracks in-flight statereset()clears local hook stateonSuccess,onError, andonSettledare available on every mutation hook
useCreate, useUpdate, and useUpsert also expose data with the last returned Model<T>.
Result Summary
useRead and useList
dataisLoadingisFetchingerrorquery(...)refetch()reset()
useCount and useDistinct
dataisLoadingerrorquery()refetch()reset()
useCreate, useUpdate, useUpsert
dataisPendingerrormutate(...)reset()
useDelete
isPendingerrormutate(id)reset()
Active Record Integration
Data returned from useList and useRead is backed by Model<T> wrappers from @web-ts-toolkit/access-router-client.
That means you can edit loaded models directly and persist with save():
const { data, refetch } = useList({ listParams: { pageSize: 20 } });
async function rename(id: string, name: string) {
const organization = data.find((entry) => entry._id === id);
if (!organization) return;
organization.name = name;
const result = await organization.save();
if (result.success) {
refetch();
}
}
Use explicit mutation hooks when you want local pending and error state around a specific workflow.
Notes
- These hooks do not implement shared caching, deduping, or invalidation.
- They are thin stateful wrappers over
ModelServicefrom@web-ts-toolkit/access-router-client. requestConfigis forwarded to the underlying client request and can include headers,signal, and transport-specific options.- If you need cache orchestration, use these services underneath a query library.