createResource
Resources are a collection of RestEndpoints that operate on a common
data by sharing a schema
Usage
export class Todo extends Entity {
id = '';
title = '';
completed = false;
pk() {
return this.id;
}
static key = 'Todo';
}
const TodoResource = createResource({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos/:id',
schema: Todo,
});
const todo = useSuspense(TodoResource.get, { id: '5' });
const todos = useSuspense(TodoResource.getList);
controller.fetch(TodoResource.getList.push, {
title: 'finish installing reactive data client',
});
controller.fetch(
TodoResource.update,
{ id: '5' },
{ ...todo, completed: true },
);
controller.fetch(TodoResource.partialUpdate, { id: '5' }, { completed: true });
controller.fetch(TodoResource.delete, { id: '5' });
Arguments
{
path: string;
schema: Schema;
urlPrefix?: string;
body?: any;
searchParams?: any;
paginationField?: string;
optimistic?: boolean;
Endpoint?: typeof RestEndpoint;
} & EndpointExtraOptions
path
Passed to RestEndpoint.path
schema
Passed to RestEndpoint.schema
urlPrefix
Passed to RestEndpoint.urlPrefix
searchParams
Passed to RestEndpoint.searchParams for getList and getList.push
body
Passed to RestEndpoint.body for getList.push update and partialUpdate
optimistic
true makes all mutation endpoints optimistic
Endpoint
Class used to construct the members.
EndpointExtraOptions
Members
These provide the standard CRUD endpointss common in REST APIs. Feel free to customize or add new endpoints based to match your API.
get
- method: 'GET'
- path:
path - schema: schema
// GET //test.com/api/abc/xyz
createResource({ urlPrefix: '//test.com', path: '/api/:group/:id' }).get({
group: 'abc',
id: 'xyz',
});
Commonly used with useSuspense(), Controller.invalidate
getList
- method: 'GET'
- path:
shortenPath(path)- Removes the last argument:
createResource({ path: '/:first/:second' }).getList.path === '/:first';
createResource({ path: '/:first' }).getList.path === '/';
- Removes the last argument:
- schema: new schema.Collection([schema])
// GET //test.com/api/abc?isExtra=xyz
createResource({ urlPrefix: '//test.com', path: '/api/:group/:id' }).getList({
group: 'abc',
isExtra: 'xyz',
});
Commonly used with useSuspense(), Controller.invalidate
getList.push
getList.push- method: 'POST'
- schema:
getList.schema.push
// POST //test.com/api/abc
// BODY { "title": "winning" }
createResource({
urlPrefix: '//test.com',
path: '/api/:group/:id',
}).getList.push({ group: 'abc' }, { title: 'winning' });
Commonly used with Controller.fetch
update
- method: 'PUT'
- path:
path - schema:
schema
// PUT //test.com/api/abc/xyz
// BODY { "title": "winning" }
createResource({ urlPrefix: '//test.com', path: '/api/:group/:id' }).update(
{ group: 'abc', id: 'xyz' },
{ title: 'winning' },
);
Commonly used with Controller.fetch
partialUpdate
- method: 'PATCH'
- path:
path - schema:
schema
// PATCH //test.com/api/abc/xyz
// BODY { "title": "winning" }
createResource({
urlPrefix: '//test.com',
path: '/api/:group/:id',
}).partialUpdate({ group: 'abc', id: 'xyz' }, { title: 'winning' });
Commonly used with Controller.fetch
delete
- method: 'DELETE'
- path:
path - schema: new schema.Invalidate(schema)
- process:
(value, params) {
return value && Object.keys(value).length ? value : params;
},
// DELETE //test.com/api/abc/xyz
createResource({ urlPrefix: '//test.com', path: '/api/:group/:id' }).delete({
group: 'abc',
id: 'xyz',
});
Commonly used with Controller.fetch
extend()
createResource builds a great starting point, but often endpoints need to be further customized.
extend() is polymorphic with three forms:
Batch extension of known members
export const CommentResource = createResource({
path: '/repos/:owner/:repo/issues/comments/:id',
schema: Comment,
}).extend({
getList: { path: '/repos/:owner/:repo/issues/:number/comments' },
update: { body: { body: '' } },
});
Adding new members
export const UserResource = createGithubResource({
path: '/users/:login',
schema: User,
}).extend('current', {
path: '/user',
schema: User,
});
Function form (to get BaseResource/super)
export const IssueResource= createResource({
path: '/repos/:owner/:repo/issues/:number',
schema: Issue,
pollFrequency: 60000,
searchParams: {} as IssueFilters | undefined,
}).extend(BaseResource => ({
search: BaseResource.getList.extend({
path: '/search/issues\\?q=:q?%20repo\\::owner/:repo&page=:page?',
schema: {
results: {
incompleteResults: false,
items: BaseIssueResource.getList.schema.results,
totalCount: 0,
},
link: '',
},
})
)});
Explore more Reactive Data Client demos
Function Inheritance Patterns
To reuse code around Resource design, you can create your own function that calls createResource().
This has similar effects as class-based inheritance.
import {
createResource,
RestEndpoint,
type EndpointExtraOptions,
type RestGenerics,
type ResourceGenerics,
type ResourceOptions,
} from '@data-client/rest';
export class AuthdEndpoint<
O extends RestGenerics = any,
> extends RestEndpoint<O> {
getRequestInit(body: any): RequestInit {
return {
...super.getRequestInit(body),
credentials: 'same-origin',
};
}
}
export function createMyResource<O extends ResourceGenerics = any>({
schema,
Endpoint = AuthdEndpoint,
...extraOptions
}: Readonly<O> & ResourceOptions) {
return createResource({
Endpoint,
schema,
...extraOptions,
}).extend({
getList: {
schema: {
results: new schema.Collection([schema]),
total: 0,
limit: 0,
skip: 0,
},
},
});
}
Explore more Reactive Data Client demos