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()

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

            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';
            const sortedUsers = new Query(
            new schema.All(User),
            (entries, { asc } = { asc: false }) => {
            const sorted = [...entries].sort((a, b) => a.name.localeCompare(b.name));
            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

                      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';
                      const usersByAdmin = new Query(
                      new schema.All(User),
                      (entries, { isAdmin }: { isAdmin?: boolean } = {}) => {
                      if (isAdmin === undefined) return entries;
                      return entries.filter(user => user.isAdmin === isAdmin);
                      },
                      );
                      function UsersPage() {
                      useFetch(UserResource.getList);
                      const users = useCache(usersByAdmin, { isAdmin: 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

                                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

                                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.