Entities
Entities are foundational blocks of building datastores and APIs. Each entity is mapped to a table in a relation database and to a collection in a Document database. Entities are defined in yaml, and are exposed by Data API block will expose them for CRUD (Create, Update, Retrieve, Delete) operations on REST, GraphQL protocols.
Defining Simple Entity
Section titled “Defining Simple Entity”The simplest entity will have a name and few fields.
task.yaml
entities:- name: task fields: - name: id # Primary key field type: int validations: - type: required - type: unique message: should me unique for all entries - type: final message: field cannot be updated - name: name type: string validations: - type: required # validation marking the field as mandatory message: name is a required field # error message to send when validation fails - name: description type: string - name: duedate type: date - name: priority type: priorityDefault Traits
Section titled “Default Traits”All entities created on the kis.ai platform have the below fields, injected. into them through the traits kisai.common and kisai.softdelete, as they have the attribute applytoall set to true.
| Trait | Field | Description |
|---|---|---|
| kisai.common | createdby | this will have the id of the user creating the current row of the entity |
| kisai.common | createdon | this will have the timestamp when the user created the current row of the entity |
| kisai.common | updatedby | this will have the id of the user updating the current row of the entity |
| kisai.common | updatedon | this will have the timestamp when the user updated the current row of the entity |
| kisai.softdelete | deletedby | this will have the id of the user soft deleted the current row of the entity |
| kisai.softdelete | deletedon | this will have the timestamp when the user soft deleted the current row of the entity |
YAML Definition of common traits
traits:- name: common applytoall: true fields: - name: createdby type: string defaultvalue: contextget("user") validations: - type: final - type: length min: 5 max: 255 - name: createdon type: timestamp defaultvalue: currentTimestamp() validations: - type: final - name: updatedby type: string computed: contextget("user") validations: - type: length min: 5 max: 255 - name: updatedon type: timestamp computed: currentTimestamp()- name: softdelete applytoall: true fields: - name: deletedby type: string validations: - type: writeonce - type: length min: 5 max: 255 - name: deletedon type: timestamp validations: - type: writeonceDefault Ordering
Section titled “Default Ordering”When querying the data for an entity, it by default ordered by id. However, default ordering by another fields might be needed when fetching multiple rows of an entity. Here default-order ensures that data retrieved always follows the default order.
Order by clauses in queries will override default-order definition.
entities:inherits: kisai.iddefault-order: name ascfields:- name: outlets- name: address- name: gpsEvaluation Lifecycle
Section titled “Evaluation Lifecycle”Entity has its own internal lifecycle and has an evaluation sequence. Every time a row is created or updated, this lifecycle of evaluation occurs
[Flowchart showing: defaultvalue → transforms → computed → validations]
Default Values
Section titled “Default Values”Fields can have either static or dynamic default values. In the below example, ulid() function gives a new ulid every time its called, or a static value New York
person.yaml
entities:- name: person fields: - name: id type: ulid defaultvalue: ulid() validations: - type: required - type: unique message: id field needs to be unique - type: final message: id field cannot be updated - name: city type: string defaultvalue: > 'New York'Common Default Functions
| Function | Description |
|---|---|
| ulid() | gives new ULIDs |
| currentTimestamp() | gives the current timestamp |
| contextget(“user”) | returns the current user id from the request context |
Idempotent
Section titled “Idempotent”Entities can be marked as idempotent by adding idempotent is true, then the id field is not generated on the server side and it is expected from the client side.
Computed Fields
Section titled “Computed Fields”Computed fields allow developers to create dynamic fields, whose values are not provided by the user but are computed from other fields or functions. In the example below takehome is a computed field.
salary.yaml
entities:- name: salary fields: - name: empid type: ulid - name: basic type: int - name: bonus type: int - name: professionaltax type: int - name: incometax type: int - name: takehome type: int computed: basic + bonus - (professionaltax + incometax)Transforms
Section titled “Transforms”Most times the input values need some transformations before inserting into the datastore, like trimming, capitalizing or replace symbols etc. Listed below are all the supported transforms.
| Field Datatype | Payload Datatype | Transform | Examples |
|---|---|---|---|
| string | Any | tostring - convert any type to string | fields: - name: firstname type: string transform: - type: tostring |
| string | string | trim - trim spaces from beginning and end ltrim - trim spaces from beginning rtrim - trim spaces from end lowercase - convert value to lowercase uppercase - make the entire string uppercase titlecase - make the entire string titlecase sentencecase - make the entire string sentence case prefix - add a prefix to the string suffix - add a suffix to the string replace - replace a char or substring in the string with a character or substring emptyasnull - replace an empty string with null value | fields: - name: firstname type: string transform: - type: trim - name: middlename type: string transform: - type: emptyasnull - name: lastname type: string transform: - type: ltrim |
| string | string | prefix - add a prefix to the string suffix - add a suffix to the string replace - replace a char or substring in the string with a character or substring | fields: - name: uid type: string transform: - type: prefix value: UID- - type: uppercase - name: slug type: string transform: - type: replace find: " " replace: "-" |
| int bigint | string float double | tointeger - convert string, float or double to integer | fields: - name: age type: int transform: - type: tointeger |
| double | string int float | todouble - convert string, integer or float to double | fields: - name: amount type: double transform: - type: todouble |
| date datetime timestamp | string | todate - convert string to date todatetime - convert string to datetime totimestamp - convert string to datetime with tz | - name: orderdate type: date transform: - type: todate format: yyyy-mm-dd - name: birthtime type: datetime transform: - type: todatetime format: yyyy-mm-ddTHH:mm:ss - name: transactionclosurets type: timestamp transform: - type: totimestamp format: yyyy-mm-ddTHH:mm:ss+5:30 |
Validations
Section titled “Validations”Validations are a critical part of all API. Validations in kis.ai are isomorphic
| Validation | Description | Examples |
|---|---|---|
| required | marks that the field value should be provided | fields: - name: lastname type: string validations: - type: required message: lastname is a required field |
| unique | marks that the field values should be unique | fields: - name: employeeid type: string validations: - type: required message: employeeid is a required field - type: unique message: employeeid should be unique |
| final | marks that the field value can be set only once and then it becomes readonly | fields: - name: uid type: string validations: - type: final message: uid field cannot be changed |
| readonly | marks that the field value can only be read and cannot be updated. Usually such values also have computed to allow them to be computed from other fields. | fields: - name: amount type: double defaultvalue: 0 - name: discount type: double computed: amount * 0.1 # 10% discount validations: - type: readonly message: you cannot update discount directly |
| writeonly | field can be updated but the value stored cannot be read. Usually used to store tokens or PII data or answers to private questions used to authenticate users. This fields still can be used for checking value, but cannot be used to read, and cannot be used as input for computed fields | fields: - name: secretanswer01 type: string validations: - type: writeonly message: you cannot read from this field |
| writeonce | field can be updated only once but the value stored cannot be read. | fields: - name: secretanswer01 type: string validations: - type: writeonly message: you can write only once to this field |
| length | length of the value is validated against the lower bound or upper bound | fields: - name: firstname type: string validations: - type: length min: 3 max: 50 message: firstname should be between 3 and 50 characters - name: interestedtopics type: array(string) validations: - type: length min: 3 max: 10 message: you should select 3 to 10 interested topics |
| minmax | Validate that the numerical value is between a lowerbound and upperbound | fields: - name: age type: int validations: - type: minmax min: 18 max: 150 message: age should be between 18 and 150 years - name: rating type: int validations: - type: minmax min: 1 message: rating should be at minimum of 1 |
Compliance
Section titled “Compliance”Compliances are additional constraints that you can add to the field to make it compliant with specific values.
| Compliance | Description | Examples |
|---|---|---|
| nolog | Ensures that field is not logged in log files including audit logs. Eg: Social Security Number, Credit Card | fields: - name: creditcard type: string compliances: - type: nolog |
| hash | The value provided will not be stored directly. A destructive hash is stored. Typically used for passwords | fields: - name: secondpassword type: string compliances: - type: hash algorithm: SHA-512/256 |
| mask | when querying this field data is masked and to see the real value, the user should have unmask privilege on this field | fields: - name: creditcard type: string compliances: - type: mask value: "XXX___" |
| tokenize | stores the tokenized data in the field. User should have untokenize privilege to see the actual value | fields: - name: creditcard type: string compliances: - type: tokenize value: creditcard-unique |
| encrypt | stores the encrypted data in the field. User should have decrypt privilege to see the decrypted value | fields: - name: securenote type: string compliances: - type: encrypt algorithm: aes-256 vault-key: securenotekey |
| pii | marks the field as Personally Identifiable Information and will be stored encrypted and nolog compliance is applied automatically. The user should have showpii privilege on this field to see the data | fields: - name: socialsecuritynumber type: string compliances: - type: pii algorithm: aes-256 vault-key: securenotekey |
Defining Related Entities
Section titled “Defining Related Entities”Entities in real world are some times nested, most times related and complex. When modeling the real world in entities, relations are must have. Data API block has the concept of references to define relations between entities. These relationships dictate how data is connected and ensures data integrity.
1. One-to-One (1:1)
Section titled “1. One-to-One (1:1)”A one-to-one relationship occurs when a single record in one entity is associated with a single record in another entity. This type of relationship is less common but useful for splitting a large entity into smaller ones or for security purposes.
Employee and Salary Breakup
Each employee has one salary breakup, and one salary breakup is linked to one user.
entities:- name: employee inherits: kisai.uid fields: - name: firstname - name: lastname- name: salarybreakup inherits: kisai.id fields: - name: employeeid type: ulid validations: - type: required - name: base type: int - name: bonus type: int - name: equity type: intreferences:- name: employee_salarybreakup parent: employee.id child: salarybreakup.employeeid type: onetoone2. One-to-Many (1:M)
Section titled “2. One-to-Many (1:M)”A one-to-many relationship is the most common type of relationship between entities. It occurs when a single record of one entity is related to multiple records in another entity.
Customer and Orders:
One customer can place multiple orders, but each order is linked to only one customer.
entities:- name: customer inherits: kisai.uid fields: - name: firstname - name: lastname - name: address- name: orders inherits: kisai.id fields: - name: customerid type: ulid validations: - type: required - name: itemcount type: int - name: totalmrp type: int - name: totaltax type: int - name: totaldiscount type: int - name: totalpaid type: intreferences:- name: customer_orders parent: customer.id child: orders.customerid type: onetomany3. Many-to-Many (M:N)
Section titled “3. Many-to-Many (M:N)”A many-to-many relationship occurs when multiple records in one table are related to multiple records in another table. This relationship is typically implemented using a junction (or associative) table that breaks down the many-to-many relationship into two one-to-many relationships.
Students and Courses: Students can enroll in multiple courses, and each course can have multiple students.
entities:- name: student inherits: kisai.uid fields: - name: firstname - name: lastname - name: grade- name: course inherits: kisai.id fields: - name: name type: string - name: credits type: int - name: start type: date - name: end type: datereferences:- name: student_courses parent: student.id child: course.id type: manytomanyCustomizing the hidden entity
Sometimes you need to store additional details in the hidden entity, then you use the use customentity
entities:- name: customer inherits: kisai.uid fields: - name: firstname - name: lastname- name: infrastructure: inherits: kisai.id fields: - name: machinename - name: ip - name: total_memory type: bigint validations: - type: required - name: total_storage type: bigint validations: - type: required - name: total_cores type: bigint validations: - type: required- name: customerinfrastructureallocation inherits: kisai.id default-order: id desc primary-key: id fields: - name: id type: ulid defaultvalue: ulid() validations: - type: required - type: unique message: id field needs to be unique - type: final message: id field cannot be updated - name: customerid type: ulid validations: - type: required - name: infrastructureid type: ulid validations: - type: required - name: meta type: object - name: allocated_memory type: bigint validations: - type: required - name: allocated_storage type: bigint validations: - type: required - name: allocated_cores type: bigint validations: - type: requiredreferences:- name: employee_courses parent: employee.id child: course.id type: manytomany customentity: name: customerinfrastructureallocation parentfield: customerid childfield: infrastructureid4. Self-Referencing (Recursive)
Section titled “4. Self-Referencing (Recursive)”A self-referencing relationship occurs when a entity is related to itself. This is useful for hierarchical data structures such as organizational charts or family trees.
Employees: An employee can be a manager of other employees, creating a recursive relationship within the same table.
entities:- name: employee inherits: kisai.uid fields: - name: firstname - name: lastname - name: managerid type: ulidreferences:- name: employee_manager parent: employee.id child: employee.managerid type: onetomany