Pular para o conteúdo

Validação e condições

Usado com propriedades de string ou número para renderizar seletores:

import type { StringProperty } from "@rebasepro/types";
// Simple: each value mapped to its label
const simple: StringProperty = {
type: "string",
name: "Status",
enum: { draft: "Draft", published: "Published", archived: "Archived" }
};
// As a list, which keeps the order you write
const listed: StringProperty = {
type: "string",
name: "Status",
enum: [
{ id: "draft", label: "Draft" },
{ id: "published", label: "Published" },
{ id: "archived", label: "Archived" }
]
};
// With colors (for Kanban columns and chips)
const colored: StringProperty = {
type: "string",
name: "Status",
enum: [
{ id: "draft", label: "Draft", color: "grayDark" },
{ id: "published", label: "Published", color: "greenDark" },
{ id: "archived", label: "Archived", color: "orangeDark" }
]
};

Uma lista simples de valores, enum: ["draft", "published", "archived"], não faz parte do tipo. Uma configuração JavaScript que escreva uma continua funcionando: o enum do banco de dados, os tipos gerados e o dropdown do admin a leem como esses valores, cada um sendo o seu próprio rótulo.

Cada regra reside no bloco validation da propriedade, e quais regras uma propriedade aceita depende do seu tipo — min conta caracteres em uma string, compara grandezas em um número e conta elementos em um array.

properties: {
title: {
type: "string",
name: "Title",
validation: { required: true, min: 2, max: 200 }
},
slug: {
type: "string",
name: "Slug",
validation: {
unique: true,
matches: /^[a-z0-9-]+$/,
matchesMessage: "Lowercase letters, digits and hyphens only",
trim: true,
lowercase: true
}
},
readingMinutes: {
type: "number",
name: "Reading time",
validation: { integer: true, moreThan: 0, max: 120 }
}
}

O servidor verifica o validation na fronteira da requisição — REST, o socket realtime e MCP — sobre o que o chamador enviou, antes de o beforeSave executar, e responde com um 400 nomeando todo campo que falhar, a uma escrita que infrinja uma regra. O código do servidor que escreve em processo é confiável e não é verificado contra essas regras: rebase.data e rebase.dataAsAdmin em uma function ou cron job, context.data em um callback, um script de seed. O que vale ali é o que o banco de dados impõe — required (NOT NULL), unique (um índice único), o tipo de um enum e a largura de uma coluna varchar ou char. Qualquer outra coisa que seu código de servidor grave, verifique no próprio código que a escreve.

O painel de administração verifica as mesmas regras antes de salvar, no formulário do registro e no editor inline da tabela da coleção. Uma célula da tabela julga um valor assim que você o edita: um valor armazenado que falha em uma regra adicionada depois não mostra nenhum erro na tabela até que alguém edite essa célula.

Regra Tipo Descrição
required boolean O valor deve estar presente. Em uma string, "" conta como ausente: a API a recusa em toda escrita, uma criação ou uma atualização, com a mesma violação de required (a menos que a coleção tenha um beforeSave, que pode preenchê-la)
requiredMessage string O que é dito quando não está: pelo formulário de admin, e como a message da entrada required do campo no 400 da API. O NOT NULL do banco de dados não carrega nenhuma mensagem
unique boolean Apenas uma linha na tabela pode ter este valor. O formulário de admin também o verifica ao salvar um valor que a edição alterou, de modo que um duplicado é indicado no seu próprio campo antes da gravação
uniqueInArray boolean Apenas uma entrada por array pode contê-lo. Aplica-se a um filho direto de uma propriedade array, ou a um filho de primeiro nível de um map dentro de um. Apenas formulário de admin — não aplicado pela API ou pelo banco de dados.
Regra Tipo Descrição
min / max number Quantidade mínima / máxima de caracteres, inclusivo. Com columnType: "varchar" ou "char", max também dimensiona a coluna (VARCHAR(max)), portanto diminuí-lo em uma tabela que já contém linhas mais longas é uma migração que o banco de dados recusa, e o editor de coleções avisa quando uma edição o diminui. A coluna padrão é TEXT, que max deixa sem limite — o limite então fica apenas a cargo da verificação de requisição do servidor
length number Exatamente essa quantidade de caracteres — um código de país, uma referência de largura fixa
matches string | RegExp Um padrão com o qual todo o valor deve corresponder. Uma string é compilada por requisição, portanto, uma que não compilar é rejeitada na inicialização em vez de silenciosamente se tornar nenhuma regra
matchesMessage string O que o formulário exibe quando matches falha
trim boolean Remove espaços em branco no início e no fim antes de salvar. Uma transformação, não uma verificação — altera o que é gravado, sendo a solução para “a mesma tag duas vezes, uma com espaço no final”. Apenas formulário de admin — não aplicado pela API ou pelo banco de dados.
lowercase / uppercase boolean Converte o caso (maiúsculas/minúsculas) do valor antes de salvar, assim como trim. Apenas formulário de admin — não aplicado pela API ou pelo banco de dados.

Formatos de email e URL não são regras de validação. email e url são flags na própria propriedade, ao lado de type — declarações sobre os dados a partir dos quais o contrato OpenAPI é gerado, em vez do comportamento do formulário:

properties: {
email: { type: "string", name: "Email", email: true },
website: { type: "string", name: "Website", url: true }
}

Consulte Propriedades de string.

Regra Tipo Descrição
min / max number Menor / maior valor aceito, inclusivo
moreThan / lessThan number As versões exclusivas de min e max
positive / negative boolean Estritamente maior / menor que zero. 0 é rejeitado por ambos — use min: 0 para permitir
integer boolean Sem parte fracionária. Não altera o tipo de coluna; columnType altera
Regra Tipo Descrição
min / max Date Data mínima / máxima aceita, inclusivo. Um instante fixo, não “hoje”
Regra Tipo Descrição
min / max number Quantidade mínima / máxima de elementos, inclusivo. Elementos, não caracteres

Você pode tornar os campos dinâmicos para que eles reajam aos valores da entidade. Existem duas maneiras de fazer isso:

Você pode usar a propriedade conditions para definir regras declarativas de JSON Logic que podem ser serializadas e modificadas visualmente no editor de coleções.

price: {
type: "number",
name: "Price",
conditions: {
disabled: { "==": [{ "var": "values.is_free" }, true] },
required: { "!=": [{ "var": "values.is_free" }, true] },
min: 0,
clearOnDisabled: true // Set to null if field gets disabled
}
}

O objeto de condições dá acesso a:

  • disabled, disabledMessage, clearOnDisabled, hidden, readOnly
  • required, requiredMessage, min, max
  • defaultValue
  • enumConditions, allowedEnumValues, excludedEnumValues
  • referencePath, referenceFilter
  • canAddElements, sortable (para arrays)
  • acceptedFiles, maxFileSize (para uploads)

disabledMessage é a frase que o usuário vê no lugar do controle. Sem isso, um campo desativado não explica nada, o que é a diferença entre um formulário que está orientando alguém e um que o está recusando.

Para comportamentos complexos que não podem ser expressos via JSON Logic, você pode usar dynamicProps, que avalia uma função Javascript.

price: {
type: "number",
name: "Price",
dynamicProps: ({ values, authController }) => ({
admin: { disabled: values.is_free === true || !authController.user?.roles?.includes("admin") },
validation: values.is_free ? {} : { required: true, min: 0 }
})
}

O mesmo aviso se aplica, e ainda mais enfaticamente: a função é empacotada no painel e chamada a cada renderização do formulário. Apenas formulário de admin — não aplicado pela API ou pelo banco de dados.