Interface design principles for package APIs — where configuration, behavior, and state belong across schema, endpoint, and hook layers. Use when adding or changing options, behavior, or state in packages/*.
71
89%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Resources are a collection of methods for a given data model. Entities and Schemas declaratively define the data model. Endpoints are the methods on that data. Both should be as declarative as possible — describing what, not how.
These concerns separate into three layers. Each changes for different reasons, is used in different contexts, and composes independently. Place new configuration or behavior in the layer whose reason to change matches.
| Layer | Declares… | Changes when… | Examples |
|---|---|---|---|
| Schema (Entity, Collection, Query) | Data model and relationships | Data shape or identity changes | pk(), schema, merge(), process(), indexes |
| Endpoint (Endpoint, RestEndpoint, resource) | Methods on that data | API contract or cache policy changes | path, method, schema, pollFrequency, getOptimisticResponse() |
| Hook / Composable (useSuspense, useDLE, useQuery) | Data dependencies at point of use | UI interaction pattern changes | Suspense, { data, loading, error }, subscription lifecycle |
If it describes data identity or relationships → Schema. Schemas are protocol-agnostic and UI-agnostic. An Entity's pk(), merge(), and nested schema work identically whether data arrives via REST, WebSocket, or controller.set().
If it describes how to obtain or mutate data → Endpoint. Declare path, method, schema, and policy — not imperative fetch logic. Endpoints are UI-agnostic: the same endpoint powers React, Vue, SSR, or imperative controller.fetch().
If it describes how the user experiences data → Hook. Loading states, error delivery, suspense, subscriptions, and reactivity adapters are UI concerns. Hooks co-locate data dependencies where data is rendered — not at the top of a tree. They are thin adapters over Controller, not duplicate cache logic.
If it's shared policy (expiry, staleness, optimistic updates) → EndpointExtraOptions. These live on endpoints as per-request policy, but are implemented by Controller/managers, not hooks.
Favor declarative configuration that the framework interprets over imperative code users must write:
pk(), schema relationships, merge() policy — the store handles normalization.path, method, schema, optimistic — the framework handles fetching, caching, and updates.ctrl.fetch(TodoResource.partialUpdate, { id }, { completed }) — a single typed statement describing the action, not manual cache updates or invalidation cascades.The endpoint + args input pattern is the stable interface shared by all hooks and controller.fetch. This enables one endpoint definition to power useSuspense, useDLE, useCache, useSubscription, or imperative controller.fetch — without the endpoint knowing which UI pattern consumes it.
When adding a new capability, ask:
extend() override.optimistic flag sufficesControllerController — it serves all consumers (React, Vue, imperative)useSuspense where data is renderedreactive/data-client
b475613
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.