Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
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
- Host: GitHub
- URL: https://github.com/reactive/data-client
- Owner: reactive
- License: apache-2.0
- Created: 2019-02-15T03:37:05.000Z (almost 6 years ago)
- Default Branch: master
- Last Pushed: 2024-10-29T08:57:50.000Z (about 1 month ago)
- Last Synced: 2024-10-29T09:56:23.815Z (about 1 month ago)
- Topics: expo, fetch, hooks, normalized, normalizr, react, react-native, reactive-programming, rest, sse-client, state-management, suspense, typescript
- Language: TypeScript
- Homepage: https://dataclient.io
- Size: 321 MB
- Stars: 1,939
- Watchers: 18
- Forks: 93
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE
- Roadmap: docs/ROADMAP.md
Awesome Lists containing this project
README
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, ExpoGoSchema 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] 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 Type
Mutable
Schema
Description
QueryableObject
โ
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
โ