Skip to main content

Entity Features

Creating an Entity

entity(name, body), where:

  • name: entity name.

  • body: object containing entity structure: fields and methods.

  • return: a Herbs entity class.

Example:

const { entity, field, id } = require('@herbsjs/herbs')

const Customer =
entity('Customer', {
id: id(Number),
name: field(String),
isVIP() {
...
}
})

const aCustomer = new Customer()

Fields

Defines the fields (properties) of an entity.

field(type, options), where:

  • type: a scalar (JavaScript) type or a custom type.

    Ex: field(String).

    In order to define a field that holds an array instead of a single value use [type].

    Ex: field([String]).

  • options: defines the field options (ex: validations, default value, etc.).

    Ex: field(Number, { validation: { presence: true } })

  • return: an entity field definition instance.

Example:

const Order = 
entity('Order', {
id: id(Number, {
validation: { presence: true, length: { minimum: 3 } }
}),
date: field(Date),
items: field([OrderItems]),
...
})

ID Fields

Defines a fields as ID of an entity.

// The explicit way
const User =
entity('User', {
id: field(Number, { isId: true }),
...
})

// The short way
const User =
entity('User', {
id: id(Number),
...
})

It is allowed to have one or many ID fields on a entity.

To access the metadata:


const user = new User()

//should be equals ```true```
user.__proto__.meta.schema.id.options.isId

Scalar types

A field in an entity can have basic types, the same as those used by JavaScript:

Number: double-precision 64-bit binary format IEEE 754 value

String: a UTF‐16 character sequence

Boolean: true or false

Date: represents a single moment in time in a platform-independent format

Object: the Object class represents a generic reference value type

const User = 
entity('User', {
name: field(String),
lastAccess: field(Date),
accessCount: field(Number),
hasAccess: field(Boolean)
})

Entity type

For complex types, with deep relationship between entities, a field can have an entity type:

const Plan = 
entity('Plan', {
...
})

const User =
entity('User', {
...
plan: field(Plan)
})

Array field type

In order to define a field that holds an array instead of a single value use field([String]).

const Post =
entity('Post', {
...
tags: field([String])
})

For complex types, with deep relationship between entities, a field can contain a list of entity type:

const Plan = 
entity('Plan', {
...
})

const User =
entity('User', {
...
plans: field([Plan])
})

Default value

It is possible to define a default value when an entity instance is initiated.

const User = 
entity('User', {
...
hasAccess: field(Boolean, { default: false })
})


const user = new User()
user.hasAccess // false

If the default value is a function, it will call the function and it will return the value as default value:

const User = 
entity('User', {
...
hasAccess: field(Boolean, { default: () => false })
})


const user = new User()
user.hasAccess // false

When not specified, the default value (for scalar and entity types) is undefined.

For reference types (like arrays) you must use functions in order to create a new object for every instance.

❌ Wrong: items: field([Item], { default: [] })

✅ Right: items: field([Item], { default: () => [] })

Methods

Defines the methods (actions) of an entity.

Example:

const User =
entity('User', {
...
role: field(String),
hasAccess() { return this.role === "admin" },
})

const aUser = new User()
aUser.role = "admin"
const canAccess = aUser.hasAccess()

Validation

The values of an entity fields can be validated against the fields types or values validations.

Check Validation

instance.isValid(): returns true if all the validations passed. It calls .validate() internally.

instance.validate(): processes the validation and loads all errors into .errors.

instance.errors: list of errors.

const User = 
entity('User', {
name: field(String),
})

const user = new User()
user.name = "Joe"
user.validate()
user.errors // {}
user.isValid() // true

Except IDs

It is possible to ignore ID field validation using .isValid({ exceptIDs: true }). It can be useful for creation use cases when the entity IDs star as null or undefined and will be generated later from the database.

Type Validation

It is possible to validate the type of a value .

const Plan = 
entity('Plan', {
...
monthlyCost: field(Number),
})

const User =
entity('User', {
name: field(String),
plan: field(Plan)
})

const user = new User()
user.name = 42
user.plan.monthlyCost = true
user.validate()
user.errors // { name: [ wrongType: 'String' ], plan: { monthlyCost: [ wrongType: 'Number' ] } }
user.isValid() // false

You can also simplify your validation method using isValid() method that executes a validation for you entity and returns true/false in a single execution.


const Plan =
entity('Plan', {
...
monthlyCost: field(Number),
})

const plan = new Plan()
plan.plan.monthlyCost = true
plan.isValid() // false
plan.errors // { monthlyCost: [ wrongType: 'Number' ] }

Value Validation

It is possible to validate values through pre-existing validators or custom validators.

Use { validation: ... } to specify the validators.

const User = 
entity('User', {
...
password: field(String, {
validation: {
presence: true,
length: { minimum: 6 }
}
})
})

const user = new User()
user.password = '1234'
user.validate()
user.errors // { password: [ { isTooShort: 6 } ] }
user.isValid() // false

Metadata

Fields metadata

It is possible to access the fields metadata of an entity through the schema.fields static property:


const Order = entity('Order', {
id: id(Number, {
validation: { presence: true, length: { minimum: 3 } }
}),
date: field(Date),
items: field([OrderItems])
})

const orderFields = Order.schema.fields

console.log(orderFields)
// [
// Field {
// name: 'id',
// type: [Function: Number],
// options: { validation: [Object], isId: true },
// _validations: null
// },
// Field {
// name: 'date',
// type: [Function: Date],
// options: {},
// _validations: null
// },
// Field {
// name: 'items',
// type: [ [class OrderItem extends BaseEntity] ],
// options: {},
// _validations: null
// }
// ]

Ids metadata

It is possible to access the ids metadata of an entity by the schema.ids static property:


const User = entity('User', {
id: id(Number),
})

const userIds = User.schema.ids

console.log(userIds)

// [
// Field {
// name: 'id',
// type: [Function: Number],
// options: { isId: true },
// _validations: null
// }
// ]

Serialization

fromJSON(value)

Returns a new instance of a entity based on a object or a string.

const User = 
entity('User', {
name: field(String)
})

// from object
const user = User.fromJSON({ name: 'Beth'})
// or string
const user = User.fromJSON(`{ "name": "Beth"}`)

By default, fromJSON serializes only keys that have been defined in the entity as fields. If you want to add other keys during serialization, use .fromJSON(data, { allowExtraKeys: true }).

By default, fromJSON default field values will be applied for keys not present in value.

JSON.stringify(entity)

To serialize an entity to JSON string use JSON.stringify or entity.toJSON function.

const json = JSON.stringify(user) // { "name": "Beth" }

By default, toJSON serializes only keys that have been defined in the entity. If you want to add other keys during serialization, use entity.toJSON({ allowExtraKeys: true }).

Instance Type Check - Entity.parentOf

Checks if a instance is the same type from its parent entity class (similar to instanceOf).

        const User = entity('User', {...})
const Customer = entity('Customer', {...})

const aUser = new User()
const aCustomer = new Customer()

User.parentOf(aUser) // true
User.parentOf(aCustomer) // false

Entity Type Check - entity.isEntity

Checks if an object is a Gotu Entity class.

    const { entity } = require('@herbsjs/herbs')

const AnEntity = entity('A entity', {})

const instance1 = new AnEntity()

entity.isEntity(AnEntity) // true
entity.isEntity(Object) // false