diff --git a/.changeset/few-eyes-remember.md b/.changeset/few-eyes-remember.md
new file mode 100644
index 000000000000..1e1acf867498
--- /dev/null
+++ b/.changeset/few-eyes-remember.md
@@ -0,0 +1,7 @@
+---
+'@data-client/normalizr': patch
+'@data-client/rest': patch
+'@data-client/ssr': patch
+---
+
+docs: Update readme
diff --git a/packages/normalizr/README.md b/packages/normalizr/README.md
index f50402936c6f..6416414c659a 100644
--- a/packages/normalizr/README.md
+++ b/packages/normalizr/README.md
@@ -2,7 +2,7 @@
[![CircleCI](https://circleci.com/gh/reactive/data-client/tree/master.svg?style=shield)](https://circleci.com/gh/data-clientdata-clients)
[![Coverage Status](https://img.shields.io/codecov/c/gh/reactive/data-client/master.svg?style=flat-square)](https://app.codecov.io/gh/data-clientdata-clients?branch=master)
-[![npm version](https://img.shields.io/npm/v/@data-client/normalizr.svg?style=flat-square)](https://www.npmjs.com/package/data-clients/normalizr) [![npm downloads](https://img.shields.io/npm/dmdata-clientks/normalizr.svg?style=flat-square)](https://www.npmjs.com/packagdata-clientoks/normalizr)
+[![npm version](https://img.shields.io/npm/v/@data-client/normalizr.svg?style=flat-square)](https://www.npmjs.com/package/data-clients/normalizr) [![npm downloads](https://img.shields.io/npm/dm/@data-client/normalizr.svg?style=flat-square)](https://www.npmjs.com/packagdata-clientoks/normalizr)
## Install
diff --git a/packages/rest/README.md b/packages/rest/README.md
index 49ec5bfec0fd..a35ff9e6b31e 100644
--- a/packages/rest/README.md
+++ b/packages/rest/README.md
@@ -1,4 +1,4 @@
-# Data Client for REST
+# 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)
@@ -8,76 +8,106 @@
[![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)
-Extensible CRUD patterns for REST APIs.
-
-**[📖Read The Docs](https://dataclient.io/rest)** | [🎮Github Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/github-app?file=src%2Fresources%2FIssue.tsx)
+**[📖Read The Docs](https://dataclient.io/rest)** | [🎮Todo Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/todo-app?file=src%2Fresources%2FTodoResource.ts) | [🎮Github Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/github-app?file=src%2Fresources%2FIssue.tsx)
-### Simple TypeScript definition
+## RestEndpoint
+
+Simplify TypeScript fetch functions with [RestEndpoint](https://dataclient.io/rest/api/RestEndpoint)
```typescript
-import { Entity, createResource } from '@data-client/rest';
+const getTodo = new RestEndpoint({
+ urlPrefix: 'https://jsonplaceholder.typicode.com',
+ path: '/todos/:id',
+});
+```
-class Article extends Entity {
- id: number | undefined = undefined;
- title = '';
- body = '';
+[RestEndpoint](https://dataclient.io/rest/api/RestEndpoint) infers [path-to-regex](https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp)
+argument types, enabling enforcement of function calls
- pk() {
- return this.id;
- }
-}
-const ArticleResource = createResource({
- path: '/articles/:id',
- schema: Article,
-});
+```typescript
+// signature requires id!
+const todo = await getTodo({ id: 5 });
```
-[Entity](https://dataclient.io/rest/api/Entity) defines a data model.
-[createResource](https://dataclient.io/rest/api/createResource) creates a [collection](https://dataclient.io/rest/api/createResource#members)
-of six [RestEndpoints](https://dataclient.io/rest/api/RestEndpoint)
+It automatically handles REST concepts like JSON serialization, consolidated error handling and more.
-[RestEndpoints](https://dataclient.io/rest/api/RestEndpoint) are functions (and more) that return a Promise.
-Both call parameters and return value are [automatically inferred](https://dataclient.io/rest/api/RestEndpoint#typing) from
-the options used to construct them.
+## Resources
-`path` is a templating language using [path-to-regex compile](https://github.com/pillarjs/path-to-regexp#compile-reverse-path-to-regexp).
+Simplify related CRUD endpoints with [Resources](https://dataclient.io/rest/api/createResource)
-### [Standard CRUD Endpoints](https://dataclient.io/rest/api/createResource#members)
+[Resources](https://dataclient.io/rest/api/createResource) are a collection of `methods` for a given `data model`.
-#### Reads
+[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_]() on
+that data.
```typescript
-const article = useSuspense(ArticleResource.get, { id: 5 });
-const articles = useSuspense(ArticleResource.getList);
+class Todo extends Entity {
+ id = 0;
+ userId = 0;
+ title = '';
+ completed = false;
+ pk() {
+ return `${this.id}`;
+ }
+}
+const TodoResource = createResource({
+ 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/createResource#members):
+
```typescript
-const [article, setArticle] = useState();
-useEffect(() => {
- setArticle(await ArticleResource.get({ id: 5 }));
-}, []);
+// 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 });
```
-#### Mutates
+## Free React Integration
+
+No need for any custom hooks. All endpoints are 100% compatible with
+the fastest way to fetch and mutate REST data: [Reactive Data Client](https://dataclient.io)
+
+#### [Rendering](https://dataclient.io/docs/getting-started/data-dependency)
```typescript
-const ctrl = useController();
-const updateArticle = data => ctrl.fetch(ArticleResource.update, { id }, data);
-const partialUpdateArticle = data =>
- ctrl.fetch(ArticleResource.partialUpdate, { id }, data);
-const createArticle = data => ctrl.fetch(ArticleResource.getList.push, data);
-const deleteArticle = data => ctrl.fetch(ArticleResource.delete, { id });
+const todo = useSuspense(TodoResource.get, { id: 5 });
+const todoList = useSuspense(TodoResource.getList);
```
-### Use with Node
+#### [Mutations](https://dataclient.io/docs/getting-started/mutations)
```typescript
-const article = await ArticleResource.get({ id: 5 });
-const articles = await ArticleResource.getList();
+const ctrl = useController();
+const updateTodo = data => ctrl.fetch(TodoResource.update, { id }, data);
+const partialUpdateTodo= data =>
+ ctrl.fetch(TodoResource.partialUpdate, { id }, data);
+const createTodo = data => ctrl.fetch(TodoResource.getList.push, data);
+const deleteTodo = data => ctrl.fetch(TodoResource.delete, { id });
```
### [Programmatic queries](https://dataclient.io/rest/api/Query)
@@ -102,8 +132,6 @@ const articlesDescending = useCache(sortedArticles, { asc: false });
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.
-Version 5.x can be used for older TypeScript versions.
-
### Prior Art
- [Backbone Model](https://backbonejs.org/#Model)
diff --git a/packages/ssr/README.md b/packages/ssr/README.md
index e5b359f959ca..3df48c349eb1 100644
--- a/packages/ssr/README.md
+++ b/packages/ssr/README.md
@@ -1,9 +1,8 @@
-# Data Client Server Side Rendering helpers
+# [![Reactive Data Client](./data_client_logo_and_text.png?sanitize=true)](https://dataclient.io)
[![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/ssr.svg?style=flat-square)](https://www.npmjs.com/package/@data-client/ssr)
-[![bundle size](https://img.shields.io/bundlephobia/minzip/@data-client/ssr?style=flat-square)](https://bundlephobia.com/result?p=@data-client/ssr)
[![npm version](https://img.shields.io/npm/v/@data-client/ssr.svg?style=flat-square)](https://www.npmjs.com/package/@data-client/ssr)
[![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)
@@ -11,81 +10,24 @@
**[📖Read The Docs](https://dataclient.io/docs/guides/ssr)** |
-[🎮NextJS SSR Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/nextjs?file=pages%2FAssetPrice.tsx)
+[🎮NextJS Demo](https://stackblitz.com/github/reactive/data-client/tree/master/examples/nextjs?file=pages%2FAssetPrice.tsx)
-Hydrate/dehydration utilities for [Data Client](https://dataclient.io)
+Hydrate/dehydration utilities for Server Side Rendering with the [Reactive Data Client](https://dataclient.io)
-## Server side
+## Usage
-```tsx
-import express from 'express';
-import { renderToPipeableStream } from 'react-dom/server';
-import {
- createPersistedStore,
- createServerDataComponent,
-} from '@data-client/ssr';
+Integration with
-const rootId = 'react-root';
+- [NextJS](https://dataclient.io/docs/guides/ssr#nextjs)
+- Anansi
+ ```bash
+ npx @anansi/cli hatch my-project
+ ```
+- [ExpressJS](https://dataclient.io/docs/guides/ssr#express-js)
-const app = express();
-app.get('/*', (req: any, res: any) => {
- const [ServerCacheProvider, useReadyCacheState, controller] =
- createPersistedStore();
- const ServerDataComponent = createServerDataComponent(useReadyCacheState);
-
- controller.fetch(NeededForPage, { id: 5 });
-
- const { pipe, abort } = renderToPipeableStream(
- ]}
- rootId={rootId}
- >
- {children}
- ,
-
- {
- onCompleteShell() {
- // If something errored before we started streaming, we set the error code appropriately.
- res.statusCode = didError ? 500 : 200;
- res.setHeader('Content-type', 'text/html');
- pipe(res);
- },
- onError(x: any) {
- didError = true;
- console.error(x);
- res.statusCode = 500;
- pipe(res);
- },
- },
- );
- // Abandon and switch to client rendering if enough time passes.
- // Try lowering this to see the client recover.
- setTimeout(abort, 1000);
-});
-
-app.listen(3000, () => {
- console.log(`Listening at ${PORT}...`);
-});
-```
-
-## Client
-
-```tsx
-import { hydrateRoot } from 'react-dom';
-import { awaitInitialData } from '@data-client/ssr';
-
-const rootId = 'react-root';
-
-awaitInitialData().then(initialState => {
- hydrateRoot(
- document.getElementById(rootId),
- {children},
- );
-});
-```
+For more details, see the [Server Side Rendering docs page](https://dataclient.io/docs/guides/ssr).
## NextJS
@@ -191,6 +133,78 @@ export default class MyDocument extends DataClientDocument {
+## Express JS
+
+### Server side
+
+```tsx
+import express from 'express';
+import { renderToPipeableStream } from 'react-dom/server';
+import {
+ createPersistedStore,
+ createServerDataComponent,
+} from '@data-client/ssr';
+
+const rootId = 'react-root';
+
+const app = express();
+app.get('/*', (req: any, res: any) => {
+ const [ServerCacheProvider, useReadyCacheState, controller] =
+ createPersistedStore();
+ const ServerDataComponent = createServerDataComponent(useReadyCacheState);
+
+ controller.fetch(NeededForPage, { id: 5 });
+
+ const { pipe, abort } = renderToPipeableStream(
+ ]}
+ rootId={rootId}
+ >
+ {children}
+ ,
+
+ {
+ onCompleteShell() {
+ // If something errored before we started streaming, we set the error code appropriately.
+ res.statusCode = didError ? 500 : 200;
+ res.setHeader('Content-type', 'text/html');
+ pipe(res);
+ },
+ onError(x: any) {
+ didError = true;
+ console.error(x);
+ res.statusCode = 500;
+ pipe(res);
+ },
+ },
+ );
+ // Abandon and switch to client rendering if enough time passes.
+ // Try lowering this to see the client recover.
+ setTimeout(abort, 1000);
+});
+
+app.listen(3000, () => {
+ console.log(`Listening at ${PORT}...`);
+});
+```
+
+### Client
+
+```tsx
+import { hydrateRoot } from 'react-dom';
+import { awaitInitialData } from '@data-client/ssr';
+
+const rootId = 'react-root';
+
+awaitInitialData().then(initialState => {
+ hydrateRoot(
+ document.getElementById(rootId),
+ {children},
+ );
+});
+```
+
## API
### createPersistedStore(managers) => [ServerCacheProvider, useReadyCacheState, controller, store]
diff --git a/packages/ssr/data_client_logo_and_text.png b/packages/ssr/data_client_logo_and_text.png
new file mode 100644
index 000000000000..29147f41dbb1
Binary files /dev/null and b/packages/ssr/data_client_logo_and_text.png differ