@code-net/react-hal-query
v1.0.4
Published
React hooks for querying HAL (Hypertext Application Language) resources using TanStack React Query (React Query). This package provides seamless integration between HAL APIs and React applications with built-in support for data fetching, mutations, and ca
Downloads
468
Readme
react-hal-query
React hooks for querying HAL (Hypertext Application Language) resources using TanStack React Query (React Query). This package provides seamless integration between HAL APIs and React applications with built-in support for data fetching, mutations, and caching.
Features
- 🪝 React hooks for HAL resource queries and mutations
- ⚡ Built on TanStack React Query for powerful data management
- 🔗 URI template support for dynamic HAL links
- 🧵 First-class TypeScript support
- 📦 Optional Next.js BFF (Backend-for-Frontend) integration
- 📋 Optional JSON Forms integration with validation
- 🎨 Material-UI support for form rendering
Installation
npm install @code-net/react-hal-query
# or
pnpm add @code-net/react-hal-queryPeer Dependencies
This package requires the following peer dependencies:
npm install @tanstack/react-query react @code-net/halOptional peer dependencies for specific features:
# For JSON Forms support
npm install @jsonforms/core @jsonforms/react ajv ajv-formats
# For Material-UI form components
npm install @mui/material
# For Next.js BFF support
npm install nextBasic Usage
Setup Provider
First, wrap your application with HalClientProvider to provide the HTTP client:
import { HalClientProvider } from '@code-net/react-hal-query';
import { QueryClientProvider, QueryClient } from '@tanstack/react-query';
const queryClient = new QueryClient();
export function App() {
return (
<QueryClientProvider client={queryClient}>
<HalClientProvider
client={async (url, config) => {
// Your custom HTTP client
return fetch(url, config);
}}
>
{/* Your app components */}
</HalClientProvider>
</QueryClientProvider>
);
}Querying Resources
Use useHalResource to fetch a single HAL resource:
import { useHalResource } from '@code-net/react-hal-query';
export function UserProfile({ userId }: { userId: string }) {
const {
data: user,
isLoading,
error,
} = useHalResource<User>({
href: '/api/users/{id}',
params: { id: userId },
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <div>Name: {user?.name}</div>;
}Querying Collections
Use useHalCollection to fetch HAL collections:
import { useHalCollection } from '@code-net/react-hal-query';
export function UsersList() {
const {
items: users,
isLoading,
error,
} = useHalCollection<User>({
href: '/api/users',
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}Mutations
Use useHalMutation to create, update, or delete resources:
import { useHalMutation } from '@code-net/react-hal-query';
import type { PostHalLink } from '@code-net/hal';
export function CreateUser() {
const createMutation = useHalMutation<User, NewUserData>({
mutationFn: async (link) => {
// Handled by hook
},
onSuccess: (newUser) => {
console.log('User created:', newUser);
},
invalidates: [{ href: '/api/users' }],
});
return (
<button
onClick={() => {
createMutation.mutate({
href: '/api/users',
method: 'POST',
body: { name: 'John Doe', email: '[email protected]' },
} as PostHalLink & { body: NewUserData });
}}
>
Create User
</button>
);
}API Reference
HalClientProvider
Provides the HTTP client for all HAL queries.
Props:
client: (url: string, config: RequestInit) => Promise<Response>- Custom HTTP client functionchildren: ReactNode- React components
useHalResource
Fetches a single HAL resource.
Options:
href: string- The resource URL or URI templateparams?: Record<string, unknown>- URI template parametersmethod?: string- HTTP method (default: 'GET')enabled?: boolean- Enable/disable the query- All TanStack React Query
UseQueryOptionsproperties
Returns:
data: TResource- The fetched resourceisLoading: boolean- Loading stateerror: Error | null- Error if the request failsrefetch: () => void- Manual refetch function- All other TanStack React Query properties
useHalCollection
Fetches a HAL collection with items.
Options:
href?: string- The collection URLparams?: Record<string, unknown>- URI template parametersmethod?: string- HTTP method (default: 'GET')- All
useHalResourceoptions
Returns:
items: TItem[]- Array of items_links?: unknown- HAL links from the response- All
useHalResourcereturn properties
useHalMutation
Executes HAL mutations (POST, PUT, PATCH, DELETE).
Options:
invalidates?: HalQueryLink[] | (() => HalQueryLink[])- Queries to invalidate after mutation- All TanStack React Query
UseMutationOptionsproperties
Returns:
- All TanStack React Query mutation properties
HalQueryLink:
interface HalQueryLink {
href: string;
params?: Record<string, unknown>;
}getQueryKey
Generate cache keys for HAL resources.
const key = getQueryKey(
{ href: '/api/users/{id}', params: { id: 123 } },
1 // optional: slice parameter
);Next.js BFF Integration
Setup
import { BffApiProvider } from '@code-net/react-hal-query/next-bff';
export function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<BffApiProvider
options={{
pageToApi: (href) => `/api/${href}`,
apiToPage: (href) => href.replace('/api/', ''),
apiResourceTitle: (item) => item.title || item.name,
}}
>
{children}
</BffApiProvider>
</body>
</html>
);
}Hooks
usePageResource()- Fetch resource matching current page routeuseBffApi()- Access BFF configurationuseBffLinks()- Get current page links from API
JSON Forms Integration
Basic Usage
import { HalSchemaForm } from '@code-net/react-hal-query/jsonforms';
export function UserForm() {
return (
<HalSchemaForm<User>
href="/api/users/schema"
onSuccess={(user) => console.log('Created:', user)}
/>
);
}Material-UI Support
import { withMuiMaterial } from '@code-net/react-hal-query/jsonforms/mui-material';
// Use Material-UI components for form rendering
export const MuiHalSchemaForm = withMuiMaterial(HalSchemaForm);URI Templates
This package supports URI templates (RFC 6570) for dynamic resource URLs:
// Simple variable expansion
useHalResource({
href: '/api/users/{id}',
params: { id: 123 },
});
// Multiple parameters
useHalResource({
href: '/api/users/{id}/posts/{postId}',
params: { id: 123, postId: 456 },
});Type Safety
The package is fully typed with TypeScript:
interface User {
id: string;
name: string;
email: string;
}
// Type-safe resource fetching
const { data: user } = useHalResource<User>({
href: '/api/users/{id}',
params: { id: 'user-123' },
});
// user is typed as User
user?.name; // ✓ TypeScript knows this property exists
user?.invalid; // ✗ TypeScript errorCaching and Invalidation
Queries are automatically cached based on their URL and parameters. Invalidate caches after mutations:
const mutation = useHalMutation({
invalidates: [
{ href: '/api/users' },
{ href: '/api/users/{id}', params: { id: userId } },
],
onSuccess: () => {
// Caches for these URLs will be refetched
},
});Or use dynamic invalidation:
const mutation = useHalMutation({
invalidates: () => [{ href: `/api/users/${userId}` }`
Or use dynamic invalidation:
```tsx
const mutation = useHalMutation({
invalidates: () => [
{ href: `/api/users/${userId}` },
],
});License
See LICENSE in repository root.
Author
Bartosz Pasinski (https://dev.pasi.pl)
