Documentação
Introdução
Dê um schema a uma coleção e todo insert, update, replace e upsert vai precisar satisfazê-lo. Dados ruins nunca entram, e você fica sabendo de todos os problemas de uma vez.
app.db.createCollection('users', {
required: ['name', 'email'],
additionalProperties: false,
properties: {
name: { type: 'string', minLength: 2, maxLength: 80 },
email: { type: 'string', format: 'email' },
age: { type: ['integer', 'null'], minimum: 0, maximum: 150 },
role: { enum: ['admin', 'user'], default: 'user' },
tags: { type: 'array', items: { type: 'string' }, uniqueItems: true, maxItems: 10 },
createdAt: { type: 'date' },
address: {
type: 'object',
required: ['zip'],
properties: {
zip: { type: 'string', pattern: '^\\d{5}-\\d{3}$' },
city: { type: 'string', default: 'Recife' }
}
}
}
});
app.db.users.insertOne({ name: 'Ana', email: 'ana@example.com', address: { zip: '50000-000' } });
// armazenado como { name, email, address: { zip, city: 'Recife' }, role: 'user', _id }
Quando a validação falha
try {
app.db.users.insertOne({ name: 'A', email: 'not an email', extra: true });
} catch (error) {
error instanceof sorla.ValidationError; // true
error.name; // 'ValidationError'
error.message; // 'Invalid document: name must have at least 2 characters (and 2 more)'
error.errors;
// [
// { path: 'name', rule: 'minLength', message: 'name must have at least 2 characters' },
// { path: 'email', rule: 'format', message: 'email must be a valid email' },
// { path: 'extra', rule: 'additionalProperties', message: 'extra is not allowed' }
// ]
}
Cada problema traz o path do campo (address.zip, tags[1]), a rule que falhou e uma message
legível. As operações insertMany, updateMany e bulkWrite continuam atômicas: um único documento
inválido faz com que nada seja armazenado naquela chamada.
Palavras-chave
| Palavra-chave | Aplica-se a | Observações |
|---|---|---|
type |
qualquer | string, number, integer, boolean, object, array, null, date (um objeto Date), ou vários: ['string', 'null'] |
required |
objetos | nomes dos campos que precisam estar presentes |
properties |
objetos | um schema por campo, aninhado em qualquer profundidade |
additionalProperties |
objetos | false rejeita campos desconhecidos; um schema os valida. _id é sempre permitido |
enum |
qualquer | o valor precisa ser um dos itens da lista |
const |
qualquer | o valor precisa ser exatamente este |
minimum, maximum |
números | limites inclusivos |
exclusiveMinimum, exclusiveMaximum |
números | limites exclusivos |
minLength, maxLength |
strings | em caracteres (um emoji conta como um) |
pattern |
strings | uma expressão regular (em string) à qual o valor precisa corresponder |
format |
strings | email, date (YYYY-MM-DD), date-time (ISO 8601), uri |
items |
arrays | o schema de cada item |
minItems, maxItems |
arrays | |
uniqueItems |
arrays | sem itens repetidos |
default |
qualquer | preenchido no insert e no upsert quando o campo está ausente, inclusive dentro de objetos aninhados |
title, description, examples, $schema, $id |
qualquer | anotações, ignoradas pela validação (os formulários usam title e description) |
Palavras-chave, tipos e formatos desconhecidos lançam um erro no momento em que o schema é
definido, então um erro de digitação como tpye ou format: 'e-mail' é detectado na hora, em vez
de silenciosamente não validar nada.
Valores padrão
app.db.createCollection('tasks', {
properties: {
done: { type: 'boolean', default: false },
tags: { type: 'array', default: [] },
meta: { type: 'object', properties: { priority: { type: 'integer', default: 3 } } }
}
});
app.db.tasks.insertOne({ title: 'x', meta: {} });
// { title: 'x', meta: { priority: 3 }, done: false, tags: [], _id }
Os valores padrão são copiados (cada documento recebe o seu próprio array ou objeto) e aplicados antes
da validação, então um valor padrão conta para o required. Updates não preenchem valores padrão;
upserts preenchem.
Uma função como schema
Quando as regras dependem de vários campos, use uma função. Ela retorna true (ou nada) quando o
documento é válido; caso contrário, false, uma mensagem, ou uma lista de mensagens ou de objetos
{ path, message }:
app.db.createCollection('orders', order => {
const problems = [];
if (order.total < 0) problems.push({ path: 'total', message: 'total must not be negative' });
if (order.status === 'shipped' && !order.trackingCode) problems.push('shipped orders need a tracking code');
return problems.length ? problems : true;
});
Os problemas vêm com rule: 'custom'. Schemas em forma de função não têm valores padrão.
Verificando sem armazenar
app.db.users.validate({ name: 'Ana' });
// { valid: false, errors: [{ path: 'email', rule: 'required', message: 'email is required' }] }
app.db.users.applyDefaults({ name: 'Ana' });
// uma cópia com os valores padrão: { name: 'Ana', role: 'user' }
validate é o que os formulários usam para mostrar erros enquanto o usuário digita.
applyDefaults mostra o que o insertOne armazenaria.
Alterando o schema
app.db.users.setSchema(newSchema); // retorna a coleção; vale para as próximas escritas
app.db.users.setSchema(null); // sem schema
app.db.users.getSchema(); // o schema que você definiu, ou null
Os documentos existentes não são validados de novo quando o schema muda.