Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/reactive/data-client

Async State Management without the Management. REST, GraphQL, SSE, Websockets
https://github.com/reactive/data-client

expo fetch hooks normalized normalizr react react-native reactive-programming rest sse-client state-management suspense typescript

Last synced: 3 days ago
JSON representation

Async State Management without the Management. REST, GraphQL, SSE, Websockets

Awesome Lists containing this project

README

        



Reactive Data Client

The scalable way to build applications with [dynamic data](https://dataclient.io/docs/getting-started/mutations).

[Declarative resouce definitons](https://dataclient.io/docs/getting-started/resource) for [REST](https://dataclient.io/rest), [GraphQL](https://dataclient.io/graphql), [Websockets+SSE](https://dataclient.io/docs/concepts/managers#data-stream) and [more](https://dataclient.io/rest/api/Endpoint)

[Performant rendering](https://dataclient.io/docs/getting-started/data-dependency) in React, NextJS, React Native, ExpoGo

Schema driven. Zero updater functions.

[![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)
[![Percentage of issues still open](https://isitmaintained.com/badge/open/reactive/data-client.svg)](https://github.com/reactive/data-client/issues 'Percentage of issues still open')
[![bundle size](https://img.shields.io/bundlephobia/minzip/@data-client/react?style=flat-square)](https://bundlephobia.com/result?p=@data-client/react)
[![npm version](https://img.shields.io/npm/v/@data-client/react.svg?style=flat-square)](https://www.npmjs.com/package/@data-client/react)
[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
[![Chat](https://img.shields.io/discord/768254430381735967.svg?style=flat-square&colorB=758ED3)](https://discord.gg/35nb8Mz)

**[๐Ÿ“–Read The Docs](https://dataclient.io/docs)** ย |ย  [๐ŸGetting Started](https://dataclient.io/docs/getting-started/installation)
๐ŸŽฎ Demos: ย 
[Todo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/todo-app?file=src%2Fpages%2FHome%2FTodoList.tsx) ย |ย 
[Github Social](https://stackblitz.com/github/reactive/data-client/tree/master/examples/github-app?file=src%2Fpages%2FIssueList.tsx) ย |ย 
[NextJS SSR](https://stackblitz.com/github/reactive/data-client/tree/master/examples/nextjs?file=components%2Ftodo%2FTodoList.tsx) ย |ย 
[Websockets+SSR](https://stackblitz.com/github/reactive/coin-app/tree/master?file=src%2Fresources%2FStreamManager.ts,src%2Fapp%2FCurrencyList.tsx)

## Installation

```bash
npm install --save @data-client/react @data-client/rest @data-client/test
```

For more details, see [the Installation docs page](https://dataclient.io/docs/getting-started/installation).

## Usage

### Simple [TypeScript definition](https://dataclient.io/rest/api/Entity)

```typescript
class User extends Entity {
id = '';
username = '';
}

class Article extends Entity {
id = '';
title = '';
body = '';
author = User.fromJS();
createdAt = Temporal.Instant.fromEpochSeconds(0);

static schema = {
author: User,
createdAt: Temporal.Instant.from,
};
}
```

### Create [collection of API Endpoints](https://dataclient.io/docs/getting-started/resource)

```typescript
const UserResource = resource({
path: '/users/:id',
schema: User,
optimistic: true,
});

const ArticleResource = resource({
path: '/articles/:id',
schema: Article,
searchParams: {} as { author?: string },
optimistic: true,
paginationField: 'cursor',
});
```

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

```tsx
const article = useSuspense(ArticleResource.get, { id });
return (


{article.title} by {article.author.username}


{article.body}



);
```

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

```tsx
const ctrl = useController();
return (
<>

ctrl.fetch(ArticleResource.getList.push, { id }, article)
}
/>

ctrl.fetch(UserResource.update, { id: article.author.id }, user)
}
/>
ctrl.fetch(ArticleResource.delete, { id })}>
Delete

>
);
```

### [Subscriptions](https://dataclient.io/docs/api/useLive)

```tsx
const price = useLive(PriceResource.get, { symbol });
return price.value;
```

### [Type-safe Imperative Actions](https://dataclient.io/docs/api/Controller)

```tsx
const ctrl = useController();
await ctrl.fetch(ArticleResource.update, { id }, articleData);
await ctrl.fetchIfStale(ArticleResource.get, { id });
ctrl.expireAll(ArticleResource.getList);
ctrl.invalidate(ArticleResource.get, { id });
ctrl.invalidateAll(ArticleResource.getList);
ctrl.setResponse(ArticleResource.get, { id }, articleData);
ctrl.set(Article, { id }, articleData);
```

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

```typescript
const queryTotalVotes = new schema.Query(
new schema.Collection([BlogPost]),
posts => posts.reduce((total, post) => total + post.votes, 0),
);

const totalVotes = useQuery(queryTotalVotes);
const totalVotesForUser = useQuery(queryTotalVotes, { userId });
```

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

### [Powerful Middlewares](https://dataclient.io/docs/concepts/managers)

```ts
class LoggingManager implements Manager {
middleware: Middleware = controller => next => async action => {
console.log('before', action, controller.getState());
await next(action);
console.log('after', action, controller.getState());
};

cleanup() {}
}
```

```ts
class TickerStream implements Manager {
middleware: Middleware = controller => {
this.handleMsg = msg => {
controller.set(Ticker, { id: msg.id }, msg);
};
return next => action => next(action);
};

init() {
this.websocket = new WebSocket('wss://ws-feed.myexchange.com');
this.websocket.onmessage = event => {
const msg = JSON.parse(event.data);
this.handleMsg(msg);
};
}
cleanup() {
this.websocket.close();
}
}
```

### [Integrated data mocking](https://dataclient.io/docs/api/Fixtures)

```tsx
const fixtures = [
{
endpoint: ArticleResource.getList,
args: [{ maxResults: 10 }] as const,
response: [
{
id: '5',
title: 'first post',
body: 'have a merry christmas',
author: { id: '10', username: 'bob' },
createdAt: new Date(0).toISOString(),
},
{
id: '532',
title: 'second post',
body: 'never again',
author: { id: '10', username: 'bob' },
createdAt: new Date(0).toISOString(),
},
],
},
{
endpoint: ArticleResource.update,
response: ({ id }, body) => ({
...body,
id,
}),
},
];

const Story = () => (



);
```

### ...all typed ...fast ...and consistent

For the small price of 9kb gziped. ย ย  [๐ŸGet started now](https://dataclient.io/docs/getting-started/installation)

## Features

- [x] TS Strong [Typescript](https://www.typescriptlang.org/) inference
- [x] ๐Ÿ›Œ React [Suspense](https://dataclient.io/docs/getting-started/data-dependency#boundaries) support
- [x] ๐Ÿงต React 18 [Concurrent mode](https://dataclient.io/docs/guides/render-as-you-fetch) compatible
- [x] ๐Ÿ’ฆ [Partial Hydration Server Side Rendering](https://dataclient.io/docs/guides/ssr)
- [x] ๐ŸŽฃ [Declarative API](https://dataclient.io/docs/getting-started/data-dependency)
- [x] ๐Ÿ“ Composition over configuration
- [x] ๐Ÿ’ฐ [Normalized](https://dataclient.io/docs/concepts/normalization) caching
- [x] ๐Ÿ’ฅ Tiny bundle footprint
- [x] ๐Ÿ›‘ Automatic overfetching elimination
- [x] โœจ Fast [optimistic updates](https://dataclient.io/rest/guides/optimistic-updates)
- [x] ๐Ÿง˜ [Flexible](https://dataclient.io/docs/getting-started/resource) to fit any API design (one size fits all)
- [x] ๐Ÿ”ง [Debugging and inspection](https://dataclient.io/docs/getting-started/debugging) via browser extension
- [x] ๐ŸŒณ Tree-shakable (only use what you need)
- [x] ๐Ÿ” [Subscriptions](https://dataclient.io/docs/api/useSubscription)
- [x] โ™ป๏ธ Optional [redux integration](https://dataclient.io/docs/guides/redux)
- [x] ๐Ÿ“™ [Storybook mocking](https://dataclient.io/docs/guides/storybook)
- [x] ๐Ÿ“ฑ [React Native](https://facebook.github.io/react-native/) support
- [x] ๐Ÿ“ฑ [Expo](https://dataclient.io/docs/getting-started/installation) support
- [x] โš›๏ธ [NextJS](https://dataclient.io/docs/guides/ssr#nextjs) support
- [x] ๐Ÿšฏ [Declarative cache lifetime policy](https://dataclient.io/docs/concepts/expiry-policy)
- [x] ๐Ÿง… [Composable middlewares](https://dataclient.io/docs/api/Manager)
- [x] ๐Ÿ’ฝ Global data consistency guarantees
- [x] ๐Ÿ‡ Automatic race condition elimination
- [x] ๐Ÿ‘ฏ Global referential equality guarantees

## Examples

- Todo: [![GitHub](https://badgen.net/badge/icon/github?icon=github&label)](https://github.com/reactive/data-client/tree/master/examples/todo-app) | [![Sandbox](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/reactive/data-client/tree/master/examples/todo-app?file=src%2Fpages%2FHome%2FTodoList.tsx) | [![Edit on CodeSandbox](https://dataclient.io/img/third-party/play-codesandbox-small.svg)](https://codesandbox.io/p/devbox/github/reactive/data-client/tree/master/examples/todo-app)
- Github: [![GitHub](https://badgen.net/badge/icon/github?icon=github&label)](https://github.com/reactive/data-client/tree/master/examples/github-app) | [![Sandbox](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/reactive/data-client/tree/master/examples/github-app?file=src%2Fpages%2FIssueList.tsx)
- NextJS: [![GitHub](https://badgen.net/badge/icon/github?icon=github&label)](https://github.com/reactive/data-client/tree/master/examples/nextjs) | [![Sandbox](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/reactive/data-client/tree/master/examples/nextjs?file=components%2Ftodo%2FTodoList.tsx) | [![Edit on CodeSandbox](https://dataclient.io/img/third-party/play-codesandbox-small.svg)](https://codesandbox.io/p/devbox/github/reactive/data-client/tree/master/examples/nextjs)
- Websockets: [![GitHub](https://badgen.net/badge/icon/github?icon=github&label)](https://github.com/reactive/coin-app) | [![Sandbox](https://developer.stackblitz.com/img/open_in_stackblitz_small.svg)](https://stackblitz.com/github/reactive/coin-app/tree/master?file=src%2Fresources%2FStreamManager.ts,src%2Fapp%2FCurrencyList.tsx) | [![Website](https://badgen.net/badge/icon/production?icon=vercel&label)](https://coin-app-lake.vercel.app)

## API

### Reactive Applications

- Rendering: [useSuspense()](https://dataclient.io/docs/api/useSuspense), [useLive()](https://dataclient.io/docs/api/useLive), [useCache()](https://dataclient.io/docs/api/useCache), [useDLE()](https://dataclient.io/docs/api/useDLE), [useQuery()](https://dataclient.io/docs/api/useQuery), [useLoading()](https://dataclient.io/docs/api/useLoading), [useDebounce()](https://dataclient.io/docs/api/useDebounce), [useCancelling()](https://dataclient.io/docs/api/useCancelling)
- Event handling: [useController()](https://dataclient.io/docs/api/useController) returns [Controller](https://dataclient.io/docs/api/Controller)



Method
Subject




Fetch


ctrl.fetch
Endpoint + Args


ctrl.fetchIfStale
Endpoint + Args


Expiry


ctrl.expireAll
Endpoint


ctrl.invalidate
Endpoint + Args


ctrl.invalidateAll
Endpoint


ctrl.resetEntireStore
Everything


Set


ctrl.set
Schema + Args


ctrl.setResponse
Endpoint + Args


ctrl.setError
Endpoint + Args


ctrl.resolve
Endpoint + Args


Subscription


ctrl.subscribe
Endpoint + Args


ctrl.unsubscribe
Endpoint + Args


- Components: [<DataProvider/>](https://dataclient.io/docs/api/DataProvider), [<AsyncBoundary/>](https://dataclient.io/docs/api/AsyncBoundary), [<ErrorBoundary/>](https://dataclient.io/docs/api/ErrorBoundary), [<MockResolver/>](https://dataclient.io/docs/api/MockResolver)
- Data Mocking: [Fixture](https://dataclient.io/docs/api/Fixtures#successfixture), [Interceptor](https://dataclient.io/docs/api/Fixtures#interceptor), [renderDataHook()](https://dataclient.io/docs/api/renderDataHook)
- Middleware: [LogoutManager](https://dataclient.io/docs/api/LogoutManager), [NetworkManager](https://dataclient.io/docs/api/NetworkManager), [SubscriptionManager](https://dataclient.io/docs/api/SubscriptionManager), [PollingSubscription](https://dataclient.io/docs/api/PollingSubscription), [DevToolsManager](https://dataclient.io/docs/api/DevToolsManager)

### [Define Data](https://dataclient.io/docs/getting-started/resource)

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

Data model

Data Type
Mutable
Schema
Description
Queryable

Object
โœ…
Entity, EntityMixin
single unique object
โœ…

โœ…
Union(Entity)
polymorphic objects (A | B)
โœ…

๐Ÿ›‘
Object
statically known keys
๐Ÿ›‘

Invalidate(Entity)
delete an entity
๐Ÿ›‘

List
โœ…
Collection(Array)
growable lists
โœ…

๐Ÿ›‘
Array
immutable lists
๐Ÿ›‘

โœ…
All
list of all entities of a kind
โœ…

Map
โœ…
Collection(Values)
growable maps
โœ…

๐Ÿ›‘
Values
immutable maps
๐Ÿ›‘

any

Query(Queryable)
memoized custom transforms
โœ