1. Defining Schemas
Define schemas to represent the JSON returned by an endpoint. Compose these
to represent the data expected.
Object
- Entity - represents a single unique object (denormalized)
- new Union(Entity) - polymorphic objects (A | B)
{[key:string]: Schema} - immutable objects
- new Invalidate(Entity|Union) - to delete an Entity
List
- new Collection([Schema]) - mutable/growable lists
[Schema] - immutable lists
- new All(Entity|Union) - list all Entities of a kind
Map
new Collection(Values(Schema)) - mutable/growable maps
- new Values(Schema) - immutable maps
Derived / selector pattern
new Query(Queryable) - memoized programmatic selectors
const queryRemainingTodos = new Query(
TodoResource.getList.schema,
entries => entries.filter(todo => !todo.completed).length,
);
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() → pk() → if existing: mergeWithStore() which calls shouldUpdate() and maybe shouldReorder() + merge(); metadata via mergeMetaWithStore().
- Denormalize order:
createIfValid() → validate() → fromJS().
4. Union Types (Polymorphic Schemas)
To define polymorphic resources (e.g., events), use Union and a discriminator field.
import { Union } from '@data-client/rest'; // also available from @data-client/endpoint
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. Supplementary Endpoints (enrich existing entities)
When an endpoint returns partial or differently-shaped data for an entity already in cache
(e.g., a metadata endpoint, a stats endpoint, a lazy-load expansion endpoint),
use the same Entity as the schema — don't create a wrapper entity.
See partial-entities for patterns and examples.
6. 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.
7. 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 directory:
- Entity - Normalized data class
- Collection - Mutable/growable lists
- Union - Polymorphic schemas
- Query - Programmatic selectors
- Invalidate - Delete entities
- Values - Map schemas
- All - List all entities of a kind
- Array - Immutable list schema
- Object - Object schema
- schema - Schema overview
- relational-data - Relational data guide
- computed-properties - Computed properties guide
- partial-entities - Partial entities guide
- side-effects - Side effects guide
- sorting-client-side - Client-side sorting guide
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: data-client-schema3description: Define data schemas - Entity, Collection, Union, Query, pk/primary key, normalize/denormalize, relational/nested data, polymorphic types, Invalidate, Values Use when this capability is needed.4---56## 1. Defining Schemas78Define [schemas](references/schema.md) to represent the JSON returned by an endpoint. Compose these9to represent the data expected.1011### Object1213- [Entity](references/Entity.md) - represents a single unique object (denormalized)14- [new Union(Entity)](references/Union.md) - polymorphic objects (A | B)15- `{[key:string]: Schema}` - immutable objects16- [new Invalidate(Entity|Union)](references/Invalidate.md) - to delete an Entity1718### List1920- [new Collection([Schema])](references/Collection.md) - mutable/growable lists21- `[Schema]` - immutable lists22- [new All(Entity|Union)](references/All.md) - list all Entities of a kind2324### Map2526- `new Collection(Values(Schema))` - mutable/growable maps27- [new Values(Schema)](references/Values.md) - immutable maps2829### Derived / selector pattern3031- [new Query(Queryable)](references/Query.md) - memoized programmatic selectors32 ```ts33 const queryRemainingTodos = new Query(34 TodoResource.getList.schema,35 entries => entries.filter(todo => !todo.completed).length,36 );37 ```3839 ```ts40 const groupTodoByUser = new Query(41 TodoResource.getList.schema,42 todos => Object.groupBy(todos, todo => todo.userId),43 );44 ```4546---4748## 2. Entity best practices4950- Every `Entity` subclass **defines defaults** for _all_ non-optional serialised fields.51- Override `pk()` only when the primary key ≠ `id`.52- `pk()` return type is `number | string | undefined`53- Override `Entity.process(value, parent, key, args)` to insert fields based on args/url54- `static schema` (optional) for nested schemas or deserialization functions55 - When designing APIs, prefer nesting entities5657---5859## 3. Entity lifecycle methods6061- Normalize order: `process()` → [validate()](references/validation.md) → `pk()` → if existing: `mergeWithStore()` which calls `shouldUpdate()` and maybe `shouldReorder()` + `merge()`; metadata via `mergeMetaWithStore()`.62- Denormalize order: `createIfValid()` → [validate()](references/validation.md) → `fromJS()`.6364---6566## 4. **Union Types (Polymorphic Schemas)**6768To define polymorphic resources (e.g., events), use [Union](references/Union.md) and a discriminator field.6970```typescript71import { Union } from '@data-client/rest'; // also available from @data-client/endpoint7273export abstract class Event extends Entity {74 type: EventType = 'Issue'; // discriminator field is shared75 /* ... */76}77export class PullRequestEvent extends Event { /* ... */ }78export class IssuesEvent extends Event { /* ... */ }7980export const EventResource = resource({81 path: '/users/:login/events/public/:id',82 schema: new Union(83 {84 PullRequestEvent,85 IssuesEvent,86 // ...other event types...87 },88 'type', // discriminator field89 ),90});91```9293---9495## 5. Supplementary Endpoints (enrich existing entities)9697When an endpoint returns partial or differently-shaped data for an entity already in cache98(e.g., a metadata endpoint, a stats endpoint, a lazy-load expansion endpoint),99use the **same Entity** as the schema — don't create a wrapper entity.100101See [partial-entities](references/partial-entities.md) for patterns and examples.102103---104105## 6. Best Practices & Notes106107- Always set up `schema` on every resource/entity/collection for normalization108- Normalize deeply nested or relational data by defining proper schemas109- Use `Entity.schema` for client-side joins110- Use `Denormalize<>` type from rest/endpoint/graphql instead of InstanceType<>. This will handle all schemas like Unions, not just Entity.111112## 7. Common Mistakes to Avoid113114- Don't forget to use `fromJS()` or assign default properties for class fields115- Manually merging or 'enriching' data; instead use `Entity.schema` for client-side joins116117# References118119For detailed API documentation, see the [references](references/) directory:120121- [Entity](references/Entity.md) - Normalized data class122- [Collection](references/Collection.md) - Mutable/growable lists123- [Union](references/Union.md) - Polymorphic schemas124- [Query](references/Query.md) - Programmatic selectors125- [Invalidate](references/Invalidate.md) - Delete entities126- [Values](references/Values.md) - Map schemas127- [All](references/All.md) - List all entities of a kind128- [Array](references/Array.md) - Immutable list schema129- [Object](references/Object.md) - Object schema130- [schema](references/schema.md) - Schema overview131- [relational-data](references/relational-data.md) - Relational data guide132- [computed-properties](references/computed-properties.md) - Computed properties guide133- [partial-entities](references/partial-entities.md) - Partial entities guide134- [side-effects](references/side-effects.md) - Side effects guide135- [sorting-client-side](references/sorting-client-side.md) - Client-side sorting guide136137---138> Converted and distributed by [TomeVault](https://tomevault.io/claim/reactive) — claim your Tome and manage your conversions.139<!-- tomevault:4.0:skill_md:2026-04-11 -->