home / skills / reactive / data-client / data-client-schema
This skill helps you design and compose scalable data schemas for entities, collections, and unions, enabling normalized, polymorphic, and relational data in
npx playbooks add skill reactive/data-client --skill data-client-schemaReview the files below or copy the command above to add this skill to your agents.
---
name: data-client-schema
description: Define data schemas - Entity, Collection, Union, Query, pk/primary key, normalize/denormalize, relational/nested data, polymorphic types, Invalidate, Values
license: Apache 2.0
---
## 1. Defining Schemas
Define [schemas](references/schema.md) to represent the JSON returned by an endpoint. Compose these
to represent the data expected.
### Object
- [Entity](references/Entity.md) - represents a single unique object (denormalized)
- [new Union(Entity)](references/Union.md) - polymorphic objects (A | B)
- `{[key:string]: Schema}` - immutable objects
- [new Invalidate(Entity|Union)](references/Invalidate.md) - to delete an Entity
### List
- [new Collection([Schema])](references/Collection.md) - mutable/growable lists
- `[Schema]` - immutable lists
- [new All(Entity|Union)](references/All.md) - list all Entities of a kind
### Map
- `new Collection(Values(Schema))` - mutable/growable maps
- [new Values(Schema)](references/Values.md) - immutable maps
### Derived / selector pattern
- [new Query(Queryable)](references/Query.md) - memoized programmatic selectors
```ts
const queryRemainingTodos = new Query(
TodoResource.getList.schema,
entries => entries.filter(todo => !todo.completed).length,
);
```
```ts
const groupTodoByUser = new Query(
TodoResource.getList.schema,
todos => Object.groupBy(todos, todo => todo.userId),
);
```
---
## 2. Entity best practices
- Every `Entity` subclass **defines defaults** for _all_ non-optional serialised fields.
- Override `pk()` only when the primary key ≠ `id`.
- `pk()` return type is `number | string | undefined`
- Override `Entity.process(value, parent, key, args)` to insert fields based on args/url
- `static schema` (optional) for nested schemas or deserialization functions
- When designing APIs, prefer nesting entities
---
## 3. Entity lifecycle methods
- Normalize order: `process()` → [validate()](references/validation.md) → `pk()` → if existing: `mergeWithStore()` which calls `shouldUpdate()` and maybe `shouldReorder()` + `merge()`; metadata via `mergeMetaWithStore()`.
- Denormalize order: `createIfValid()` → [validate()](references/validation.md) → `fromJS()`.
---
## 4. **Union Types (Polymorphic Schemas)**
To define polymorphic resources (e.g., events), use [Union](references/Union.md) and a discriminator field.
```typescript
import { Union } from '@data-client/rest';
export abstract class Event extends Entity {
type: EventType = 'Issue'; // discriminator field is shared
/* ... */
}
export class PullRequestEvent extends Event { /* ... */ }
export class IssuesEvent extends Event { /* ... */ }
export const EventResource = resource({
path: '/users/:login/events/public/:id',
schema: new Union(
{
PullRequestEvent,
IssuesEvent,
// ...other event types...
},
'type', // discriminator field
),
});
```
---
## 5. Best Practices & Notes
- Always set up `schema` on every resource/entity/collection for normalization
- Normalize deeply nested or relational data by defining proper schemas
- Use `Entity.schema` for client-side joins
- Use `Denormalize<>` type from rest/endpoint/graphql instead of InstanceType<>. This will handle all schemas like Unions, not just Entity.
## 6. Common Mistakes to Avoid
- Don't forget to use `fromJS()` or assign default properties for class fields
- Manually merging or 'enriching' data; instead use `Entity.schema` for client-side joins
# References
For detailed API documentation, see the [references](references/) directory:
- [Entity](references/Entity.md) - Normalized data class
- [Collection](references/Collection.md) - Mutable/growable lists
- [Union](references/Union.md) - Polymorphic schemas
- [Query](references/Query.md) - Programmatic selectors
- [Invalidate](references/Invalidate.md) - Delete entities
- [Values](references/Values.md) - Map schemas
- [All](references/All.md) - List all entities of a kind
- [Array](references/Array.md) - Immutable list schema
- [Object](references/Object.md) - Object schema
- [schema](references/schema.md) - Schema overview
- [relational-data](references/relational-data.md) - Relational data guide
- [computed-properties](references/computed-properties.md) - Computed properties guide
- [partial-entities](references/partial-entities.md) - Partial entities guide
- [side-effects](references/side-effects.md) - Side effects guide
- [sorting-client-side](references/sorting-client-side.md) - Client-side sorting guide
This skill defines client-side data schemas and patterns for normalizing, denormalizing, and querying JSON returned by APIs. It models Entities, Collections, Unions (polymorphic types), Maps/Values, Queries (memoized selectors), and lifecycle operations like primary key resolution, validation, merge, and invalidation. The goal is predictable async state handling across REST, GraphQL, SSE, and WebSocket feeds. It pairs TypeScript types with runtime schemas to keep UI state normalized and efficient.
You declare schema objects (Entity classes, Collection, Union, Values, All, Query, etc.) that mirror the API payload shape. During normalization the system runs Entity.process → validate → pk → mergeWithStore (calling shouldUpdate/shouldReorder/merge) and stores flattened records. Denormalization reconstructs nested objects via createIfValid → validate → fromJS. Queries run memoized selectors against normalized entries to compute derived results efficiently.
How do I pick a primary key for Entity?
Use id by default; override pk() when the API uses another unique field. pk() must return number, string, or undefined for missing keys.
When should I use Union vs nested Entity schema?
Use Union when instances can be different concrete types (polymorphic). Use nested Entity schema when the shape is consistent but related and you want normalized child Entities.