npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hateoas-ts/resource

v1.4.0

Published

[![npm version](https://img.shields.io/npm/v/@hateoas-ts/resource?style=flat-square)](https://www.npmjs.com/package/@hateoas-ts/resource) [![npm downloads](https://img.shields.io/npm/dm/@hateoas-ts/resource?style=flat-square)](https://www.npmjs.com/packag

Readme

@hateoas-ts/resource

npm version npm downloads bundle size TypeScript License

Type-safe HATEOAS client for HAL APIs with automatic link navigation, caching, and middleware support.

Language: English | 中文

Installation

npm install @hateoas-ts/resource
# or
yarn add @hateoas-ts/resource
# or
pnpm add @hateoas-ts/resource

Quick 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

📚 Full API Documentation

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

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