Skip to main content

Query

Query provides programmatic access to the Rest Hooks cache while maintaining the same high performance and referential equality guarantees expected of Rest Hooks.

class Query<S extends SchemaSimple, P extends any[] = []> {
constructor(
schema: S,
process?: (entries: Denormalize<S>, ...args: P) => Denormalize<S>,
);

schema: S;
key(...args: P): string;

process: (entries: Denormalize<S>, ...args: P) => Denormalize<S>;
}

Query implements the EndpointInterface but without the fetch function, which means it can only be passed to the data binding hook useCache()

Query members

schema

Schema used to retrieve/denormalize data from the Rest Hooks cache. Most cases will use schema.All, which retrieves all entities of a given type found in the cache.

process(entries, ...args)

Takes the (denormalized) response as entries and arguments and returns the new response for use with useCache

key(...args)

Implements Endpoint.key Used to determine recomputation of memoized values.

Usage

Simplest

Fixtures
GET /users
[{"id":"123","name":"Jim"},{"id":"456","name":"Jane"}]
api/User.ts
export class User extends Entity {
id = '';
name = '';
isAdmin = false;
pk() {
return this.id;
}
}
export const UserResource = createResource({
path: '/users/:id',
schema: User,
});
UsersPage.tsx
import { Query, schema } from '@rest-hooks/rest';
import { UserResource, User } from './api/User';
const allUsers = new Query(new schema.All(User));
function UsersPage() {
useFetch(UserResource.getList);
const users = useCache(allUsers);
if (!users) return <div>No users in cache yet</div>;
return (
<div>
{users.map(user => (
<div key={user.pk()}>{user.name}</div>
))}
</div>
);
}
render(<UsersPage />);
Live Preview
Loading...
Store
    • {} 0 keys
      • {} 0 keys
        • {} 0 keys
          • {} 0 keys
            • {} 0 keys
              • 0

            Sorting & Filtering

            Fixtures
            GET /users
            [{"id":"123","name":"Jim"},{"id":"456","name":"Jane"},{"id":"777","name":"Albatras","isAdmin":true}]
            api/User.ts
            export class User extends Entity {
            id = '';
            name = '';
            isAdmin = false;
            pk() {
            return this.id;
            }
            }
            export const UserResource = createResource({
            path: '/users/:id',
            schema: User,
            });
            UsersPage.tsx
            import { Query, schema } from '@rest-hooks/rest';
            import { UserResource, User } from './api/User';
            interface Args {
            asc: boolean;
            isAdmin?: boolean;
            }
            const sortedUsers = new Query(
            new schema.All(User),
            (entries, { asc, isAdmin }: Args = { asc: false }) => {
            let sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
            if (isAdmin !== undefined)
            sorted = sorted.filter(user => user.isAdmin === isAdmin);
            if (asc) return sorted;
            return sorted.reverse();
            },
            );
            function UsersPage() {
            useFetch(UserResource.getList);
            const users = useCache(sortedUsers, { asc: true });
            if (!users) return <div>No users in cache yet</div>;
            return (
            <div>
            {users.map(user => (
            <div key={user.pk()}>{user.name}</div>
            ))}
            </div>
            );
            }
            render(<UsersPage />);
            Live Preview
            Loading...
            Store
              • {} 0 keys
                • {} 0 keys
                  • {} 0 keys
                    • {} 0 keys
                      • {} 0 keys
                        • 0

                      Aggregates

                      Fixtures
                      GET /users
                      [{"id":"123","name":"Jim"},{"id":"456","name":"Jane"},{"id":"777","name":"Albatras","isAdmin":true}]
                      api/User
                      export class User extends Entity {
                      id = '';
                      name = '';
                      isAdmin = false;
                      pk() {
                      return this.id;
                      }
                      }
                      export const UserResource = createResource({
                      path: '/users/:id',
                      schema: User,
                      });
                      UsersPage
                      import { Query, schema } from '@rest-hooks/rest';
                      import { UserResource, User } from './api/User';
                      const getUserCount = new Query(
                      new schema.All(User),
                      (entries, { isAdmin } = { }) => {
                      if (isAdmin !== undefined)
                      return entries.filter(user => user.isAdmin === isAdmin).length;
                      return entries.length;
                      },
                      );
                      function UsersPage() {
                      useFetch(UserResource.getList);
                      const userCount = useCache(getUserCount);
                      const adminCount = useCache(getUserCount, { isAdmin: true });
                      if (userCount === undefined) return <div>No users in cache yet</div>;
                      return (
                      <div>
                      <div>
                      Total users: {userCount}
                      </div>
                      <div>
                      Total admins: {adminCount}
                      </div>
                      </div>
                      );
                      }
                      render(<UsersPage />);
                      Live Preview
                      Loading...
                      Store
                        • {} 0 keys
                          • {} 0 keys
                            • {} 0 keys
                              • {} 0 keys
                                • {} 0 keys
                                  • 0

                                Client side joins

                                Even if the network responses don't nest data, we can perform client-side joins by specifying the relationship in Entity.schema

                                api/User.ts
                                export class User extends Entity {
                                id = 0;
                                name = '';
                                email = '';
                                website = '';
                                pk() {
                                return `${this.id}`;
                                }
                                }
                                export const UserResource = createResource({
                                urlPrefix: 'https://jsonplaceholder.typicode.com',
                                path: '/users/:id',
                                schema: User,
                                });
                                api/Todo.ts
                                import { User } from './User';
                                export class Todo extends Entity {
                                id = 0;
                                userId = User.fromJS({});
                                title = '';
                                completed = false;
                                pk() {
                                return `${this.id}`;
                                }
                                static schema = {
                                userId: User,
                                };
                                }
                                export const TodoResource = createResource({
                                urlPrefix: 'https://jsonplaceholder.typicode.com',
                                path: '/todos/:id',
                                schema: Todo,
                                });
                                TodoJoined.tsx
                                import { Query, schema } from '@rest-hooks/rest';
                                import { TodoResource, Todo } from './api/Todo';
                                import { UserResource, User } from './api/User';
                                const todosWithUser = new Query(
                                new schema.All(Todo),
                                (entries, { userId = 0 }) => {
                                return entries.filter(todo => todo.userId?.id === userId);
                                },
                                );
                                function TodosPage() {
                                useFetch(UserResource.getList);
                                useFetch(TodoResource.getList);
                                const todos = useCache(todosWithUser, { userId: 1 });
                                if (!todos) return <div>No Todos in cache yet</div>;
                                return (
                                <div>
                                {todos.map(todo => (
                                <div key={todo.pk()}>
                                {todo.title} by {todo.userId.name}
                                </div>
                                ))}
                                </div>
                                );
                                }
                                render(<TodosPage />);
                                Live Preview
                                Loading...
                                Store