Kinds and Metadata
The first key of a definition can be a capitalized kind. Its value is the schema's metadata, and the remaining keys are its fields:
const { Schema } = require('@alexify/metaschema');
const company = new Schema('Company', {
Registry: { scope: 'global' },
name: { type: 'string', unique: true },
});
company.kind; // 'registry'
company.scope; // 'global'
company.store; // 'persistent'Without a kind, an object definition is a struct.
Built-in kinds
| Kind | Group | Default scope | Default store | Adds an id field |
|---|---|---|---|---|
Struct | memory | local | memory | no |
Scalar | memory | local | memory | no |
Form | memory | local | memory | no |
Projection | memory | local | memory | no |
Entity | stored | application | persistent | yes |
Registry | stored | application | persistent | yes |
Dictionary | stored | application | persistent | yes |
Journal | stored | application | persistent | yes |
Details | stored | application | persistent | yes |
Relation | stored | application | persistent | yes |
View | stored | application | persistent | yes |
allow defaults to 'write' for every kind. The exported constants list the values:
const { KIND, KIND_STORED, KIND_MEMORY, SCOPE, STORE, ALLOW } = require('@alexify/metaschema');
KIND_STORED; // ['entity', 'registry', 'dictionary', 'journal', 'details', 'relation', 'view']
KIND_MEMORY; // ['struct', 'scalar', 'form', 'projection']
SCOPE; // ['application', 'global', 'local']
STORE; // ['persistent', 'memory']
ALLOW; // ['write', 'append', 'read']Stored kinds get an id field. A stored schema gains an optional string field named after it: Company gets companyId, an anonymous schema gets id.
store decides how a reference to the schema is held. A reference to a persistent schema holds its id, a reference to a memory schema embeds the record; see References.
Metadata fields
| Field | Meaning |
|---|---|
kind | the kind, in lower camel case |
scope | 'application', 'global' or 'local' |
store | 'persistent' or 'memory' |
allow | 'write', 'append' or 'read' |
parent | the parent schema of a projection |
unknown | 'reject' or 'ignore': what check does with keys the schema does not have, unless the call says otherwise |
custom | any other metadata keys |
unknown is the default of this schema's check and applies to the whole value, nested structs included; schema.check(value, { unknown }) overrides it for one call. A Form that accepts a request body with keys it does not use is the typical case:
const signup = new Schema('Signup', { Form: { unknown: 'ignore' }, login: 'string', password: 'string' });
signup.check({ login: 'a', password: 'b', remember: true }).valid; // true
signup.check({ login: 'a', password: 'b', remember: true }, { unknown: 'reject' }).errors;
// [ 'Field "Signup" has unexpected keys: remember' ]Any other value throws ERR_INVALID_OPTIONS when the schema is built.
Custom kinds
Any capitalized key works as a kind. Unknown kinds behave like memory kinds, and their metadata ends up in schema.custom:
const settings = Schema.from({ Settings: { theme: 'dark' }, mode: 'string' });
settings.kind; // 'settings'
settings.custom; // { theme: 'dark' }Projections
A projection copies selected fields from another entity in the same model:
const entities = new Map([
['Account', { Registry: {}, login: { type: 'string', unique: true }, password: 'string', email: 'string' }],
['Signin', { Projection: { schema: 'Account', fields: ['login', 'password'] } }],
]);The copied fields keep their type and options. Projections are built after every other entity, so they may refer to entities defined later in the map.
Indexes
Keys whose value holds index, primary or unique with an array of field names are indexes, not fields. They are collected in schema.indexes:
const address = new Schema('Address', {
Entity: {},
street: 'string',
building: 'string',
naturalKey: { primary: ['street', 'building'] },
byStreet: { index: ['street'] },
});
Object.keys(address.indexes); // ['naturalKey', 'byStreet']
Object.keys(address.fields); // ['street', 'building', 'addressId']many reference fields are listed in indexes as well, and stay fields. An index that names a field the schema does not have is reported in schema.warnings as missing-index-field.
Options
Top-level validate, format, parse and serialize functions are schema options, stored in schema.options. validate is run by every check (see Validation), parse and serialize by parse and serialize as the decoder and encoder of the whole record; format is kept for code that builds on the schema and never called. (The format rule of a string field, { type: 'string', format: 'email' }, is a different thing: a string format.)