Data Fetching
DBS standardizes all client-side data fetching through a set of custom SWR-based hooks. This ensures consistent loading, error, and caching behavior throughout the dashboard.
Core Hook: useData
useData is the fundamental building block — a typed SWR wrapper for any GET request.
// src/hooks/use-data.ts
import { useData } from '@/hooks/use-data';Signature
function useData<T>(
key: string | null,
fetcher: () => Promise<T>,
options?: {
transform?: (data: T) => T;
refreshInterval?: number;
revalidateOnFocus?: boolean;
}
): {
data: T | undefined;
isLoading: boolean;
error: Error | undefined;
mutate: () => void;
}Usage
Basic
import { useData } from '@/hooks/use-data';
import { usersApi } from '@/services/users/api';
export function UsersList() {
const { data: users, isLoading, error } = useData(
'users-list',
() => usersApi.getUsers()
);
if (isLoading) return <Spinner />;
if (error) return <ErrorState />;
return users?.map(user => <UserRow key={user.id} user={user} />);
}The key parameter functions as a SWR cache key. Choose unique, deterministic keys (e.g., users-list, team-${teamId}). Use the same key across components to share cached data automatically.
Full-Featured Table: useDataTable
useDataTable extends useData with server-side/client-side sort, filter, search, and pagination — all wired together.
import { useDataTable } from '@/hooks/use-data-table';
import { usersApi } from '@/services/users/api';
export function UsersTable() {
const {
data, // Current page data
isLoading,
error,
// Pagination
page,
pageSize,
totalPages,
totalItems,
setPage,
setPageSize,
// Sorting
sortKey,
sortOrder,
setSortKey,
setSortOrder,
// Filtering
search,
setSearch,
filters,
setFilter,
} = useDataTable({
key: 'users-table',
fetcher: () => usersApi.getUsers(),
defaultPageSize: 20,
searchKeys: ['name', 'email'],
defaultSort: { key: 'createdAt', order: 'desc' },
});
return (
<AppTable
data={data}
isLoading={isLoading}
columns={columns}
pagination={{ page, pageSize, totalPages, setPage }}
/>
);
}Pagination: usePagination
A lightweight hook for managing pagination state when you need it independently of the fetcher:
import { usePagination } from '@/hooks/use-pagination';
export function PaginatedList() {
const { page, pageSize, setPage, setPageSize } = usePagination({
defaultPage: 1,
defaultPageSize: 10,
});
const { data } = useData(
`items-page-${page}`,
() => itemsApi.getItems({ page, pageSize })
);
return (
<>
<ItemList items={data?.items} />
<Pagination page={page} total={data?.total} setPage={setPage} />
</>
);
}Audit Logs: useAuditLogs
A specialized hook for the audit log viewer:
import { useAuditLogs } from '@/hooks/use-audit-logs';
export function AuditLogViewer() {
const { logs, isLoading, filters, setFilter } = useAuditLogs({
defaultFilters: { userId: undefined, action: undefined },
});
return (
<LogTable
logs={logs}
isLoading={isLoading}
onFilterChange={setFilter}
/>
);
}Session: useSession
Access the current user’s session and profile:
import { useSession } from '@/hooks/use-session';
// or directly from auth-client:
import { useSession } from '@/lib/auth-client';
export function UserProfile() {
const { data: session, isPending } = useSession();
return (
<div>
<p>{session?.user.name}</p>
<p>{session?.user.email}</p>
</div>
);
}Mutating Data
For POST, PUT, DELETE requests, use a useMutation-style pattern. SWR’s mutate() function is used to invalidate cache after a successful mutation:
import { useData } from '@/hooks/use-data';
import { usersApi } from '@/services/users/api';
export function DeleteUserButton({ userId }) {
const { mutate } = useData('users-list', () => usersApi.getUsers());
const handleDelete = async () => {
await usersApi.deleteUser(userId);
await mutate(); // Re-fetch the users list
};
return <Button onClick={handleDelete}>Delete</Button>;
}Services Layer
All API calls go through typed client functions in src/services/. Never call fetch() directly in components:
| Service | Methods |
|---|---|
usersApi | getUsers(), getUser(id), assignRole(), syncUserRoles() |
teamsApi | getTeams(), createTeam(), updateTeam(), addMember(), removeMember() |
accessApi | getRoles(), getPermissions(), createRole(), updateRole(), syncPermissions() |
notificationsApi | getNotifications(), markAsRead(), markAllAsRead(), deleteNotification() |
tasksApi | createTask(), getTask(id) |