Reactive Data Client The scalable way to build applications with [dynamic data](https://dataclient.io/docs/getting-started/mutations). [Declarative resource definitions](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](https://react.dev/), [NextJS](https://nextjs.org/), [React Native](https://reactnative.dev/), [Expo](https://expo.dev/), [Vue](https://vuejs.org/) 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) [![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) **[๐Ÿ“–Read The Docs](https://dataclient.io/docs)**  |  [๐ŸGetting Started](https://dataclient.io/docs/getting-started/agent-skills)  |  [๐Ÿค–Agent Skills](https://skills.sh/reactive/data-client)
๐ŸŽฎ 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 Getting Started docs page](https://dataclient.io/docs/getting-started/agent-skills). ### Skills ```bash npx skills add reactive/data-client ``` Then run [skill](https://agentskills.io) "data-client-setup" ## 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.fromEpochMilliseconds(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, article) } /> ctrl.fetch(UserResource.update, { id: article.author.id }, user) } /> ); ``` ### [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 Query( new Collection([BlogPost]), posts => posts.reduce((total, post) => total + post.votes, 0), ); const totalVotes = useQuery(queryTotalVotes); const totalVotesForUser = useQuery(queryTotalVotes, { userId }); ``` ```typescript const groupTodoByUser = new 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 gzipped.    [๐ŸGet started now](https://dataclient.io/docs/getting-started/agent-skills) ## 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 ๐Ÿ›‘
Scalar โœ… Scalar lens-dependent entity fields โœ…
any Query(Queryable) memoized custom transforms โœ…
Lazy(Schema) deferred denormalization โœ