API Validation
Entity.validate() is called during normalization and denormalization.
undefined
indicates no error, and a string error message if there is an error.
Field check
Validation happens after Entity.process() but before Entity.fromJS(), thus operates on POJOs rather than an instance of the class.
Here we can make sure the title field is included, and of the expected type.
Fixtures
GET /article/1
{"id":"1","title":"first"}
GET /article/2
{"id":"2"}
GET /article/3
{"id":"3","title":{"complex":"second","object":5}}
▶api/Article
export class Article extends Entity { id = ''; title = ''; static validate(processedEntity) { if (!Object.hasOwn(processedEntity, 'title')) return 'missing title field'; if (typeof processedEntity.title !== 'string') return 'title is wrong type'; } } export const getArticle = new RestEndpoint({ path: '/article/:id', schema: Article, });
▶ArticlePage
🔴 Live Preview
Store▶
All fields check
validateRequired() can be used to check if all defined fields are present.
Fixtures
GET /article/1
{"id":"1","title":"first"}
GET /article/2
{"id":"2"}
GET /article/3
{"id":"3","title":{"complex":"second","object":5}}
▶api/Article
export class Article extends Entity { id = ''; title = ''; static validate(processedEntity) { return validateRequired(processedEntity, this.defaults); } } export const getArticle = new RestEndpoint({ path: '/article/:id', schema: Article, });
▶ArticlePage
🔴 Live Preview
Store▶
Partial results
Another great use of validation is mixing endpoints that return incomplete objects. This is often useful when some fields consume lots of bandwidth or are computationally expensive for the backend.
Consider using validateRequired to reduce code.
Fixtures
GET /article
[{"id":"1","title":"first"},{"id":"2","title":"second"}]
GET /article/1
{"id":"1","title":"first","content":"long","createdAt":"2011-10-05T14:48:00.000Z"}
GET /article/2
{"id":"2","title":"second","content":"short","createdAt":"2011-10-05T14:48:00.000Z"}
▶api/Article
export class ArticlePreview extends Entity { id = ''; title = ''; static key = 'Article'; } export const getArticleList = new RestEndpoint({ path: '/article', schema: [ArticlePreview], }); export class ArticleFull extends ArticlePreview { content = ''; createdAt = Temporal.Instant.fromEpochSeconds(0); static schema = { createdAt: Temporal.Instant.from, }; static validate(processedEntity) { if (!Object.hasOwn(processedEntity, 'content')) return 'Missing content'; } } export const getArticle = new RestEndpoint({ path: '/article/:id', schema: ArticleFull, });
▶ArticleDetail
🔴 Live Preview
Store▶