# @data-client/rest

> Quickly define typed REST resources and endpoints

Latest version **0.18.1** (published 2026-05-13) · Apache-2.0 license · 0 weekly downloads

## Install

```sh
npm install @data-client/rest
pnpm add @data-client/rest
yarn add @data-client/rest
bun add @data-client/rest
```

## Health

**Score 60/100 (C)** — status: active.

Positive: has types; esm support; no vulnerabilities; high maintenance score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.18.1 |
| Published | 2026-05-13 |
| First published | 2023-07-02 |
| Weekly downloads | 0 |
| License | Apache-2.0 |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Node | ^12.17 \|\| ^13.7 \|\| >=14 |
| Dependencies | 3 |
| Unpacked size | 944.6 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 2036 |
| Author | Nathaniel Tucker <me@ntucker.me> (https://github.com/ntucker) |
| Maintainers | ntucker |
| Keywords | REST, CRUD, networking, fetch, data, cache, model, resource, typescript, HTTP, path-to-regexp, path template, data fetching, data cache, api, normalized cache, subject pattern, endpoint, mutation, front-end, mobile, react-native, ios, android, web, json, async, promise |

## Links

- npm: https://www.npmjs.com/package/@data-client/rest
- Repository: https://github.com:reactive/data-client
- Homepage: https://dataclient.io/rest
- Issues: https://github.com/reactive/data-client/issues
- Funding: https://github.com/sponsors/ntucker
- npm.io page: https://npm.io/package/@data-client/rest

## Dependencies (3)

- [@babel/runtime](https://npm.io/package/@babel/runtime.md) ^7.20.0
- [path-to-regexp](https://npm.io/package/path-to-regexp.md) ^8.1.0
- [@data-client/endpoint](https://npm.io/package/@data-client/endpoint.md) ^0.18.0

## Alternatives

- [@mapbox/jsonlint-lines-primitives](https://npm.io/package/@mapbox/jsonlint-lines-primitives.md) — 5.3M weekly downloads
- [reftools](https://npm.io/package/reftools.md) — 3.5M weekly downloads
- [@hey-api/openapi-ts](https://npm.io/package/@hey-api/openapi-ts.md) — 3.5M weekly downloads
- [@mapbox/geojson-rewind](https://npm.io/package/@mapbox/geojson-rewind.md) — 2.4M weekly downloads
- [turbo-stream](https://npm.io/package/turbo-stream.md) — 1.7M weekly downloads

## Recent versions

- 0.18.1 (latest) — 2026-05-13
- 0.15.1-beta-20251201012906-ff2853c5a0da93a503b2b606c8fc724625b79308 (beta) — 2025-12-01
- 0.18.0 — 2026-05-01
- 0.17.0 — 2026-04-16
- 0.16.6 — 2026-04-15
- 0.16.5 — 2026-04-05
- 0.16.4 — 2026-04-03
- 0.16.3 — 2026-04-03
- 0.16.2 — 2026-04-03
- 0.16.1 — 2026-04-01
- 0.16.0 — 2026-03-31
- 0.15.7 — 2026-02-18
- 0.15.6 — 2026-02-15
- 0.15.5 — 2026-01-20
- 0.15.4 — 2026-01-19
- … 63 more at https://npm.io/package/@data-client/rest/versions

## README

# TypeScript HTTP definitions

[![CircleCI](https://circleci.com/gh/reactive/data-client/tree/master.svg?style=shield)](https://circleci.com/gh/reactive/data-client)
[![Coverage Status](https://img.shields.io/codecov/c/gh/reactive/data-client/master.svg?style=flat-square)](https://app.codecov.io/gh/reactive/data-client?branch=master)
[![npm downloads](https://img.shields.io/npm/dm/@data-client/rest.svg?style=flat-square)](https://www.npmjs.com/package/@data-client/rest)
[![bundle size](https://img.shields.io/bundlephobia/minzip/@data-client/rest?style=flat-square)](https://bundlephobia.com/result?p=@data-client/rest)
[![npm version](https://img.shields.io/npm/v/@data-client/rest.svg?style=flat-square)](https://www.npmjs.com/package/@data-client/rest)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Agent Skills](https://img.shields.io/badge/Agent_Skills-cc785c?style=flat-square&logo=claude&logoColor=white)](https://skills.sh/reactive/data-client)
[![Chat](https://img.shields.io/discord/768254430381735967.svg?style=flat-square&colorB=758ED3)](https://discord.gg/35nb8Mz)

<div align="center">

**[📖Read The Docs](https://dataclient.io/rest)** &nbsp;|&nbsp; [🤖Agent Skills](https://skills.sh/reactive/data-client) &nbsp;|&nbsp; [🎮Todo Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/todo-app?file=src%2Fresources%2FTodoResource.ts) &nbsp;|&nbsp; [🎮Github Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/github-app?file=src%2Fresources%2FIssue.tsx)

</div>

## RestEndpoint

Simplify TypeScript fetch functions with [RestEndpoint](https://dataclient.io/rest/api/RestEndpoint)

```typescript
const getTodo = new RestEndpoint({
  urlPrefix: 'https://jsonplaceholder.typicode.com',
  path: '/todos/:id',
});
```

[RestEndpoint](https://dataclient.io/rest/api/RestEndpoint) infers [path-to-regexp](https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp)
argument types, enabling enforcement of function calls

```typescript
// signature requires id!
const todo = await getTodo({ id: 5 });
```

It automatically handles REST concepts like JSON serialization, consolidated error handling and more.

## Resources

Simplify related CRUD endpoints with [Resources](https://dataclient.io/rest/api/resource)

[Resources](https://dataclient.io/rest/api/resource) are a collection of `methods` for a given `data model`.

[Entities](https://dataclient.io/rest/api/Entity) and [Schemas](https://dataclient.io/concepts/normalization) declaratively define the _data model_.
[RestEndpoints](https://dataclient.io/rest/api/RestEndpoint) are the [_methods_](<https://en.wikipedia.org/wiki/Method_(computer_programming)>) on
that data.

```typescript
class Todo extends Entity {
  id = 0;
  userId = 0;
  title = '';
  completed = false;
}
const TodoResource = resource({
  urlPrefix: 'https://jsonplaceholder.typicode.com',
  path: '/todos/:id',
  searchParams: {} as { userId?: string | number },
  schema: Todo,
  paginationField: 'page',
});
```

One Resource defines [seven endpoints](https://dataclient.io/rest/api/resource#members):

```typescript
// GET https://jsonplaceholder.typicode.com/todos/5
let todo5 = await TodoResource.get({ id: 5 });

// GET https://jsonplaceholder.typicode.com/todos
const todoList = await TodoResource.getList();

// GET https://jsonplaceholder.typicode.com/todos?userId=1
const userOneTodos = await TodoResource.getList({ userId: 1 });

// POST https://jsonplaceholder.typicode.com/todos
const newTodo = await TodoResource.getList.push({ title: 'my todo' });

// POST https://jsonplaceholder.typicode.com/todos?userId=1
const newUserOneTodo = await TodoResource.getList.push(
  { userId: 1 },
  { title: 'my todo' },
);

// GET https://jsonplaceholder.typicode.com/todos?userId=1&page=2
const nextPageOfTodos = await TodoResource.getList.getPage({
  userId: 1,
  page: 2,
});

// PUT https://jsonplaceholder.typicode.com/todos/5
todo5 = await TodoResource.update({ id: 5 }, { title: 'my todo' });

// PATCH https://jsonplaceholder.typicode.com/todos/5
todo5 = await TodoResource.partialUpdate({ id: 5 }, { title: 'my todo' });

// DELETE https://jsonplaceholder.typicode.com/todos/5
await TodoResource.delete({ id: 5 });
```

## Free React Integration

No need for any custom hooks. All endpoints are 100% compatible with [Reactive Data Client](https://dataclient.io)

### [Rendering](https://dataclient.io/docs/getting-started/data-dependency)

```typescript
const todo = useSuspense(TodoResource.get, { id: 5 });
const todoList = useSuspense(TodoResource.getList);
```

### [Mutations](https://dataclient.io/docs/getting-started/mutations)

```typescript
const ctrl = useController();
const updateTodo = data => ctrl.fetch(TodoResource.update, { id }, data);
const partialUpdateTodo = data =>
  ctrl.fetch(TodoResource.partialUpdate, { id }, data);
const addTodoToEnd = data => ctrl.fetch(TodoResource.getList.push, data);
const addTodoToBeginning = data =>
  ctrl.fetch(TodoResource.getList.unshift, data);
const deleteTodo = data => ctrl.fetch(TodoResource.delete, { id });
```

### [Programmatic queries](https://dataclient.io/rest/api/Query)

```tsx
const queryRemainingTodos = new Query(
  TodoResource.getList.schema,
  entries => entries.filter(todo => !todo.completed).length,
);

const allRemainingTodos = useQuery(queryRemainingTodos);
const firstUserRemainingTodos = useQuery(queryRemainingTodos, { userId: 1 });
```

```typescript
const groupTodoByUser = new Query(
  TodoResource.getList.schema,
  todos => Object.groupBy(todos, todo => todo.userId),
);
const todosByUser = useQuery(groupTodoByUser);
```

### TypeScript requirements

TypeScript is optional, but will only work with 4.0 or above. 4.1 is needed for stronger types as it
supports inferring argument types from the path templates.

### Prior Art

- [Backbone Model](https://backbonejs.org/#Model)
- [ImmutableJS Record](https://immutable-js.github.io/immutable-js/docs/#/Record)

## API

#### Networking definition

- [Endpoints](https://dataclient.io/rest/api/Endpoint): [RestEndpoint](https://dataclient.io/rest/api/RestEndpoint)
- [Resources](https://dataclient.io/docs/getting-started/resource): [resource()](https://dataclient.io/rest/api/resource), [hookifyResource()](https://dataclient.io/rest/api/hookifyResource)

<table>
<caption>
<a href="https://dataclient.io/docs/concepts/normalization">Data model</a>
</caption>
<thead>
<tr>
<th>Data Type</th>
<th>Mutable</th>
<th>Schema</th>
<th>Description</th>
<th><a href="https://dataclient.io/rest/api/schema#queryable">Queryable</a></th>
</tr>
</thead>
<tbody><tr>
<td rowSpan="4"><a href="https://en.wikipedia.org/wiki/Object_(computer_science)">Object</a></td>
<td align="center">✅</td>
<td><a href="https://dataclient.io/rest/api/Entity">Entity</a>, <a href="https://dataclient.io/rest/api/EntityMixin">EntityMixin</a></td>
<td>single <em>unique</em> object</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">✅</td>
<td><a href="https://dataclient.io/rest/api/Union">Union(Entity)</a></td>
<td>polymorphic objects (<code>A | B</code>)</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">🛑</td>
<td><a href="https://dataclient.io/rest/api/Object">Object</a></td>
<td>statically known keys</td>
<td align="center">🛑</td>
</tr>
<tr>
<td align="center"></td>
<td><a href="https://dataclient.io/rest/api/Invalidate">Invalidate(Entity)</a></td>
<td><a href="https://dataclient.io/docs/concepts/expiry-policy#invalidate-entity">delete an entity</a></td>
<td align="center">🛑</td>
</tr>
<tr>
<td rowSpan="3"><a href="https://en.wikipedia.org/wiki/List_(abstract_data_type)">List</a></td>
<td align="center">✅</td>
<td><a href="https://dataclient.io/rest/api/Collection">Collection(Array)</a></td>
<td>growable lists</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">🛑</td>
<td><a href="https://dataclient.io/rest/api/Array">Array</a></td>
<td>immutable lists</td>
<td align="center">🛑</td>
</tr>
<tr>
<td align="center"> </td>
<td><a href="https://dataclient.io/rest/api/All">All</a></td>
<td>list of all entities of a kind</td>
<td align="center">✅</td>
</tr>
<tr>
<td rowSpan="2"><a href="https://en.wikipedia.org/wiki/Associative_array">Map</a></td>
<td align="center">✅</td>
<td><a href="https://dataclient.io/rest/api/Collection">Collection(Values)</a></td>
<td>growable maps</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center">🛑</td>
<td><a href="https://dataclient.io/rest/api/Values">Values</a></td>
<td>immutable maps</td>
<td align="center">🛑</td>
</tr>
<tr>
<td rowSpan="1"><a href="https://en.wikipedia.org/wiki/Scalar_(mathematics)">Scalar</a></td>
<td align="center">✅</td>
<td><a href="https://dataclient.io/rest/api/Scalar">Scalar</a></td>
<td>lens-dependent entity fields</td>
<td align="center">✅</td>
</tr>
<tr>
<td rowSpan="2">any</td>
<td align="center"></td>
<td><a href="https://dataclient.io/rest/api/Query">Query(Queryable)</a></td>
<td>memoized custom transforms</td>
<td align="center">✅</td>
</tr>
<tr>
<td align="center"></td>
<td><a href="https://dataclient.io/rest/api/Lazy">Lazy(Schema)</a></td>
<td>deferred denormalization</td>
<td align="center">✅</td>
</tr>
</tbody></table>

---
_Source: https://npm.io/package/@data-client/rest · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
