@hateoas-ts/resource
v1.4.0
Published
[](https://www.npmjs.com/package/@hateoas-ts/resource) [](https://www.npmjs.com/packag
Readme
@hateoas-ts/resource
Type-safe HATEOAS client for HAL APIs with automatic link navigation, caching, and middleware support.
Installation
npm install @hateoas-ts/resource
# or
yarn add @hateoas-ts/resource
# or
pnpm add @hateoas-ts/resourceQuick Start
import { createClient, Entity, Collection } from '@hateoas-ts/resource';
// 1. Define your entity types
type Post = Entity<{ id: string; title: string; content: string }, { self: Post; author: User }>;
type User = Entity<{ id: string; name: string; email: string }, { self: User; posts: Collection<Post> }>;
// 2. Create client
const client = createClient({ baseURL: 'https://api.example.com' });
// 3. Navigate resources
const user = await client.go<User>('/users/123').get();
console.log(user.data.name);
// 4. Follow HATEOAS links - no URL hardcoding!
const posts = await user.follow('posts').get();
for (const post of posts.collection) {
console.log(post.data.title);
}Core Concepts
| Concept | Description | | -------------- | ----------------------------------------------------------- | | Entity | Type-safe resource definition with data, links, and actions | | Collection | Paginated list of entities with navigation links | | Resource | Represents an API endpoint with HTTP methods | | State | Contains resource data, links, and collection items | | Middleware | Intercept and modify requests/responses |
API Methods
Read Operations
// GET request (cached automatically)
const user = await client.go<User>('/users/123').get();
// Access data
console.log(user.data.name);
console.log(user.data.email);Navigation
// Follow a link to related resource
const posts = await user.follow('posts').get();
// Follow with URI template parameters
const filtered = await user.follow('posts', { page: 2, size: 10 }).get();
// Chain navigation
const author = await posts.collection[0].follow('author').get();Write Operations
// POST - Create new resource
const newPost = await user.follow('posts').post({
data: { title: 'Hello World', content: 'My first post' },
});
// PUT - Full update
await post.put({
data: { title: 'Updated Title', content: 'Updated content' },
});
// PATCH - Partial update
await post.patch({
data: { title: 'New Title' },
});
// DELETE
await post.delete();Middleware
// Add authentication
client.use(async (request, next) => {
request.headers.set('Authorization', `Bearer ${token}`);
return next(request);
});
// Add logging for specific origin
client.use(async (request, next) => {
console.log('Request:', request.url);
const response = await next(request);
console.log('Response:', response.status);
return response;
}, 'https://api.example.com');Caching
// GET requests are cached automatically
const user1 = await client.go<User>('/users/123').get();
const user2 = await client.go<User>('/users/123').get(); // From cache
// Manual cache operations
resource.clearCache();
const cached = resource.getCache();
resource.updateCache(newState);Events
const resource = client.go<User>('/users/123');
// Listen for updates
resource.on('update', (state) => {
console.log('Resource updated:', state.data);
});
// Listen for stale events (after POST/PUT/PATCH/DELETE)
resource.on('stale', () => {
console.log('Cache is stale, refetch recommended');
});
// Listen for delete
resource.on('delete', () => {
console.log('Resource was deleted');
});Collections
const postsState = await user.follow('posts').get();
// Pagination metadata
console.log(`Page ${postsState.data.page.number + 1} of ${postsState.data.page.totalPages}`);
console.log(`Total: ${postsState.data.page.totalElements} items`);
// Iterate items
for (const post of postsState.collection) {
console.log(post.data.title);
}
// Navigate pages
const nextPage = await postsState.follow('next').get();
const prevPage = await postsState.follow('prev').get();Type Definitions
Entity
import { Entity } from '@hateoas-ts/resource';
// Entity<TData, TLinks, TActions>
type User = Entity<
// TData - resource properties
{ id: string; name: string; email: string },
// TLinks - available navigation links
{
self: User;
posts: Collection<Post>;
'create-post': Post;
},
// TActions - HAL-Forms actions (optional)
{
'create-post': Post;
}
>;Collection
import { Collection } from '@hateoas-ts/resource';
// Collection automatically includes:
// - page: { size, totalElements, totalPages, number }
// - links: { first, prev, self, next, last }
type Posts = Collection<Post>;API Reference
Key Exports
| Export | Type | Description |
| ------------------ | -------- | --------------------------------------- |
| createClient | Function | Create a HATEOAS client instance |
| Entity | Type | Define entity types with data and links |
| Collection | Type | Define paginated collection types |
| Resource | Class | Resource navigation and HTTP methods |
| ResourceRelation | Class | Relationship navigation |
| State | Type | Resource state with data and links |
| FetchMiddleware | Type | Request/response middleware type |
React Integration
See @hateoas-ts/resource-react for React hooks:
import { useResource, useInfiniteCollection } from '@hateoas-ts/resource-react';
function UserProfile({ userId }) {
const { data, loading, error } = useResource<User>(`/users/${userId}`);
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <div>{data.name}</div>;
}Related Documentation
- Smart Domain DDD Architecture - Backend architecture design
- REST Principles and Agentic UI - REST architecture principles
Changelog
Version 1.4.0 (Current)
- Direct HTTP methods:
.get(),.post(),.put(),.patch(),.delete() - Request deduplication for concurrent requests
- Improved TypeScript generics
Version 1.3.0
- React integration utilities (
@hateoas-ts/resource-react) - Enhanced caching strategies
Version 1.2.0
- Basic HAL resource navigation
- Type-safe entity definitions
- Cache management
- Event system
- Middleware support
License
MIT
