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▶