- 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), );
- 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
- 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() .
- 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 ), });
- 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.
- 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