schematic-joi-model
Overview
This extendable class does two specific things:
- makes it easy to create and manage objects that conform to a specific schema
- schema is used to validate arguments passed to the constructor
- schema validation errors are thrown after being casted to a helpful representation
- all properties of arguments that match keys of the model schema are assigned to the object during construction
- makes it easy to compose models
- by simply defining the dependencies of each key, the model automatically casts the properties into those models
Installation
npm install --save schematic-joi-model
Usage Examples
For more thorough documentation and examples, please see the src/*.test.ts
files.
Basic Model
// model definition:const imageSchema = Joiobject;interface ImageConstructorParams uuid: string; path?: string;<ImageConstructorParams> public static schema = imageSchema; // model usage: uuid: 'ff8b3da2-aba8-43f5-b1fc-5609d23c684f' ; // returns: Image { uuid: 'ff8b3da2-aba8-43f5-b1fc-5609d23c684f', path: undefined } uuid: 'not a uuid' ; // throws validation error
Composed Model
Building on the above example with the Image
model:
const userSchema = Joiobject;interface UserConstructorParams uuid: string; name: string; age: number; avatar: Image;<UserConstructorParams> // properties public uuid!: string; public name!: string; public age!: number; public avatar!: Image; // metadata for validation and property assignment public static schema = userSchema; public static dependencies = avatar: Image ; uuid: '4e4cb5f9-5949-4b47-af77-d1eec4ab8fb5' name: 'bessy' age: 21 avatar: uuid: 'b4380823-917d-4e0c-bf9a-aa53fae6ff98' ;/*returns: User { uuid: '4e4cb5f9-5949-4b47-af77-d1eec4ab8fb5', name: 'bessy', age: 21, avatar: Image { uuid: 'b4380823-917d-4e0c-bf9a-aa53fae6ff98', path: undefined } }*/
Notice that the avatar property of the instance of the instantiated User
model is an instance of the Image
model
Highly Constrainted Model
Some models you may define with full data - and some may be valid to define with only a uuid and expect the full data to be derivable after the fact. In order to not allow partial information, a model can be created with the following syntax:
const fullDataSchema = Joiobject;const rootDataOnlySchema = Joiobject;const uuidOnlySchema = Joiobject;const fullSchema = Joi;interface FullDataCarConstructorParams uuid: string; make: string; model: string;interface RootDataCarConstructorParams make: string; model: string;interface UuidOnlyCarConstructorParams uuid: string;type CarConstructorParams = FullDataCarConstructorParams | RootDataCarConstructorParams | UuidOnlyCarConstructorParams;<CarConstructorParams> public uuid?: string; public make?: string; public model?: string; public static schema = fullSchema; // we can now initalize the model with only the RootDataconst car = make: 'honda' model: 'odessy' ;; // true // or with only the uuidconst car = uuid: '88d0a45a-e374-4e30-83de-6840c2ab8cc3' ;; // true // or with the full dataconst car = uuid: '88d0a45a-e374-4e30-83de-6840c2ab8cc3' make: 'honda' model: 'odessy' ;; // true // but not the partial datatry uuid: '88d0a45a-e374-4e30-83de-6840c2ab8cc3' model: 'odessy' ; // tslint:disable-line no-unused-expression throw 'should not reach here'; catch error ; // true
To Do
deduplication of type definitions
It is clear to see that we are duplicating the logic defining the types of the properties of the model objects between the Joi Schemas and Typescript Typedefs. It would be ideal if we could assign the Typescript model properties' typedefs directly from the Joi schema.
automatic dependency definitions
It would be convinient to eliminate the 'dependencies' property alltogether by somehow manipulating the Model.schema
property to pass meta-data that we could then retrieve to determine:
- that this property should be casted into a model instance
- which model instance to cast it into
Perhaps Joi has a way to add metadata to schema objects that we could then retrieve from the
Joi.describe()
method?