Pular para o conteúdo principal

Validação da API

Entity.validate() é chamado durante a normalização e a desnormalização. undefined indica que não há erro, e uma string com a mensagem de erro indica que há um erro.

Verificação de campo​

A validação acontece depois de Entity.process(), mas antes de Entity.fromJS(), e portanto opera sobre POJOs, e não sobre uma instância da classe.

Aqui podemos garantir que o campo title esteja presente e seja do tipo esperado.

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
import { Entity, RestEndpoint } from '@data-client/rest';

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
Resultado
Store▶

Verificação de todos os campos​

validateRequired() pode ser usado para verificar se todos os campos definidos estão presentes.

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
import { Entity, RestEndpoint, validateRequired } from '@data-client/rest';

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
Resultado
Store▶

Resultados parciais​

Outro ótimo uso da validação é combinar endpoints que retornam objetos incompletos. Isso costuma ser útil quando alguns campos consomem muita banda ou são computacionalmente caros para o backend.

Considere usar validateRequired para reduzir código.

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
import { Entity, RestEndpoint } from '@data-client/rest';
import { Temporal } from 'temporal-polyfill';

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.fromEpochMilliseconds(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
Resultado
Store▶