schema.Collection
Collections
are entities but for Arrays or Values.
This makes them well suited at handling mutations. You can add to Array Collections
with .push or .unshift and
Values Collections
with .assign.
RestEndpoint provides .push, .unshift, .assign
and .getPage/ .paginated() extenders when using Collections
Usage
import { getTodos } from './api/Todo'; export default function NewTodo({ userId }: { userId?: string }) { const ctrl = useController(); const [unshift, setUnshift] = React.useState(false); const handlePress = async e => { if (e.key === 'Enter') { const createTodo = unshift ? getTodos.unshift : getTodos.push; ctrl.fetch(createTodo, { title: e.currentTarget.value, userId, }); e.currentTarget.value = ''; } }; return ( <div> <input type="text" onKeyDown={handlePress} /> <label> <input type="checkbox" checked={unshift} onChange={e => setUnshift(e.currentTarget.checked)} />{' '} unshift </label> </div> ); }
Options
argsKey(...args): Object
Returns a serializable Object whose members uniquely define this collection based on Endpoint arguments.
import { schema, RestEndpoint } from '@data-client/rest';
const getTodos = new RestEndpoint({
path: '/todos',
searchParams: {} as { userId?: string },
schema: new schema.Collection([Todo], {
argsKey: (urlParams: { userId?: string }) => ({
...urlParams,
}),
}),
});
nestKey(parent, key): Object
Returns a serializable Object whose members uniquely define this collection based on the parent it is nested inside.
import { schema, Entity } from '@data-client/rest';
class Todo extends Entity {
id = '';
userId = '';
title = '';
completed = false;
pk() {
return this.id;
}
static key = 'Todo';
}
class User extends Entity {
id = '';
name = '';
username = '';
email = '';
todos: Todo[] = [];
pk() {
return this.id;
}
static key = 'User';
static schema = {
todos: new schema.Collection([Todo], {
nestKey: (parent, key) => ({
userId: parent.id,
}),
}),
};
}
nonFilterArgumentKeys?
nonFilterArgumentKeys
defines a test to determine which argument keys
are not used for filtering the results. For instance, if your API uses
'orderBy' to choose a sort - this argument would not influence which
entities are included in the response.
const getPosts = new RestEndpoint({
path: '/:group/posts',
searchParams: {} as { orderBy?: string; author?: string },
schema: new schema.Collection([Post], {
nonFilterArgumentKeys(key) {
return key === 'orderBy';
},
}),
});
For convenience you can also use a RegExp or list of strings:
const getPosts = new RestEndpoint({
path: '/:group/posts',
searchParams: {} as { orderBy?: string; author?: string },
schema: new schema.Collection([Post], {
nonFilterArgumentKeys: /orderBy/,
}),
});
const getPosts = new RestEndpoint({
path: '/:group/posts',
searchParams: {} as { orderBy?: string; author?: string },
schema: new schema.Collection([Post], {
nonFilterArgumentKeys: ['orderBy'],
}),
});
In this case, author
and group
are considered 'filter' argument keys,
which means they will influence whether a newly created should be added
to those lists. On the other hand, orderBy
does not need to match
when push
is called.
import { Entity, RestEndpoint } from '@data-client/rest'; class Post extends Entity { id = ''; title = ''; group = ''; author = ''; pk() { return this.id; } } export const getPosts = new RestEndpoint({ path: '/:group/posts', searchParams: {} as { orderBy?: string; author?: string }, schema: new schema.Collection([Post], { nonFilterArgumentKeys: /orderBy/, }), });
createCollectionFilter?
Sets a default createCollectionFilter
for addWith(),
push, unshift, and assign.
This is used by these creation schemas to determine which collections to add to.
Default:
const defaultFilter =
(urlParams: Record<string, any>, body?: Record<string, any>) =>
(collectionKey: Record<string, string>) =>
Object.entries(collectionKey).every(
([key, value]) =>
key.startsWith('order') ||
// double equals lets us compare non-strings and strings
urlParams[key] == value ||
body?.[key] == value,
);
Methods
push
A creation schema that places at the end of this collection
unshift
A creation schema that places at the start of this collection
assign
A creation schema that assigns
its members to the Collection
.
addWith(merge, createCollectionFilter): CreationSchema
Constructs a custom creation schema for this collection. This is used by push, unshift, assign and paginate
merge(collection, creation)
This merges the value with the existing collection
createCollectionFilter
This function is used to determine which collections to add to. It uses the Object returned from argsKey or nestKey to determine if that collection should get the newly created values from this schema.
Because arguments may be serializable types like number
, we recommend using ==
comparisons,
e.g., '10' == 10
(...args) =>
collectionKey =>
boolean;
Lifecycle Methods
static shouldReorder(existingMeta, incomingMeta, existing, incoming): boolean
static shouldReorder(
existingMeta: { date: number; fetchedAt: number },
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
) {
return incomingMeta.fetchedAt < existingMeta.fetchedAt;
}
true
return value will reorder incoming vs in-store entity argument order in merge. With
the default merge, this will cause the fields of existing entities to override those of incoming,
rather than the other way around.
static merge(existing, incoming): mergedValue
static merge(existing: any, incoming: any) {
return incoming;
}
static mergeWithStore(existingMeta, incomingMeta, existing, incoming): mergedValue
static mergeWithStore(
existingMeta: { date: number; fetchedAt: number },
incomingMeta: { date: number; fetchedAt: number },
existing: any,
incoming: any,
): any;
mergeWithStore()
is called during normalization when a processed entity is already found in the store.
pk: (parent?, key?, args?): pk?
pk()
calls argsKey or nestKey depending on which are specified, and
then serializes the result for the pk string.
pk(value: any, parent: any, key: string, args: readonly any[]) {
const obj = this.argsKey
? this.argsKey(...args)
: this.nestKey(parent, key);
for (const key in obj) {
if (typeof obj[key] !== 'string') obj[key] = `${obj[key]}`;
}
return JSON.stringify(obj);
}