rdc-rest
npx skills add https://github.com/reactive/data-client --skill rdc-rest
Agent 安装分布
Skill 文档
Guide: Using @data-client/rest for Resource Modeling
This project uses @data-client/rest to define, fetch, normalize, and update RESTful resources and entities in React/TypeScript apps with type safety and automatic cache management.
Always follow these patterns when generating code that interacts with remote APIs.
1. Defining Schemas
This project uses schemas to define and normalize data models with type safety and automatic cache management. Apply the skill “rdc-schema” for schema patterns. Always follow these patterns (apply the skill “rdc-schema”) when generating mutable data definitions.
2. Resources (resource())
- resource() creates a collection of RestEndpoints for CRUD operations on a common object
- Required fields:
path: pathâtoâregexp template (typed!)schema: Declarative data shape for a single item (typically Entity or Union)
- Optional:
urlPrefix: Host root, if not/searchParams: Type for query parameters (TS generic) in MyResource.getListpaginationField: Add MyResource.getList.getPage for paginationoptimistic: Boolean, when true all mutations will update optimistically, improving performancebody: Type for body parameter to MyResource.getList.push, MyResource.getList.unshift, MyResource.update, MyResource.partialUpdate
import { Entity, resource } from '@data-client/rest';
import { User } from './User';
export class Todo extends Entity {
id = 0;
user = User.fromJS();
title = '';
completed = false;
createdAt = new Date();
static key = 'Todo';
static schema = {
user: User,
createdAt: (iso: string) => new Date(iso),
}
}
export const TodoResource = resource({
urlPrefix: 'https://jsonplaceholder.typicode.com',
path: '/todos/:id',
schema: Todo,
searchParams: {} as { userId?: string | number } | undefined,
paginationField: 'page',
optimistic: true,
});
Usage
Rendering
// GET https://jsonplaceholder.typicode.com/todos/5
const todo = useSuspense(TodoResource.get, { id: 5 });
// GET https://jsonplaceholder.typicode.com/todos
const todoList = useSuspense(TodoResource.getList);
// GET https://jsonplaceholder.typicode.com/todos?userId=1
const todoListByUser = useSuspense(TodoResource.getList, { userId: 1 });
Mutations
const ctrl = useController();
// PUT https://jsonplaceholder.typicode.com/todos/5
const updateTodo = todo => ctrl.fetch(TodoResource.update, { id }, todo);
// PATCH https://jsonplaceholder.typicode.com/todos/5
const partialUpdateTodo = todo =>
ctrl.fetch(TodoResource.partialUpdate, { id }, todo);
// POST https://jsonplaceholder.typicode.com/todos
const addTodoToStart = todo =>
ctrl.fetch(TodoResource.getList.unshift, todo);
// POST https://jsonplaceholder.typicode.com/todos?userId=1
const addTodoToEnd = todo => ctrl.fetch(TodoResource.getList.push, { userId: 1 }, todo);
// DELETE https://jsonplaceholder.typicode.com/todos/5
const deleteTodo = id => ctrl.fetch(TodoResource.delete, { id });
// GET https://jsonplaceholder.typicode.com/todos?userId=1&page=2
const getNextPage = (page) => ctrl.fetch(TodoResource.getList.getPage, { userId: 1, page })
For more detailed usage, apply the skill “rdc-react” or “rdc-vue”.
3. Custom RestEndpoint patterns
/** Standâalone endpoint with custom typing */
export const getTicker = new RestEndpoint({
urlPrefix: 'https://api.exchange.coinbase.com',
path: '/products/:product_id/ticker',
schema: Ticker,
pollFrequency: 2000,
});
Typing tips
pathpathâtoâregexp template for 1st argmethodâGETâ 2nd arg = body (unlessbody: undefined)- Provide
searchParams/bodyvalues purely for type inference - Use
RestGenericswhen inheriting fromRestEndpoint
getOptimisticResponse()
getOptimisticResponse(snap, { id }) {
const article = snap.get(Article, { id });
if (!article) throw snap.abort;
return {
id,
votes: article.votes + 1,
};
}
4. RestEndpoint lifecycle methods
- Perform Fetch:
fetchResponse()âparseResponse()âprocess()- url(urlParams):
urlPrefix+path+ (searchParamsâsearchToString()) - getRequestInit(body):
getHeaders()+method+signal
- url(urlParams):
5. Extending Resources
Use .extend() to add or override endpoints.
export const IssueResource = resource({
// ...base config...
}).extend((Base) => ({
search: Base.getList.extend({
path: '/search/issues',
// ...custom schema or params...
}),
}));
6. Best Practices & Notes
- When asked to browse or navigate to a web address, actual visit the address
- Always set up
schemaon every resource/entity/collection for normalization - Prefer
RestEndpointoverresource()for defining single endpoints or when mutation endpoints don’t exist
7. Common Mistakes to Avoid
- Don’t use
resource()when mutation endpoints are not used or needed
References
For detailed API documentation, see the references directory:
- resource – Create CRUD endpoints
- RestEndpoint;_EndpointLifecycle.mdx;RestEndpoint.js – Single REST endpoint
- Entity;_entity_lifecycle_methods.mdx – Normalized data class
- Collection – Mutable lists
- schema – Schema overview
- Fixtures – Mock data for testing
- data-dependency;_useLive.mdx;_AsyncBoundary.mdx – Rendering guide
- mutations;_useLoading.mdx;_VoteDemo.mdx – Mutations guide
Guides (refer when user asks about these topics):
- auth – Authentication headers, tokens, login/logout flows
- pagination;_pagination.mdx – Cursor/offset pagination, infinite scroll
- optimistic-updates;_optimisticTransform.mdx – Instant UI feedback before server response
- network-transform – Transform responses, handle non-standard APIs
Concepts (refer when user asks about these topics):
- expiry-policy – Cache invalidation, stale data, dataExpiryLength, errorExpiryLength
- error-policy – Error handling, retry behavior, soft vs hard errors
ALWAYS follow these patterns and refer to the official docs for edge cases. Prioritize code generation that is idiomatic, type-safe, and leverages automatic normalization/caching via schema definitions.