Commodo is a set of higher order functions (HOFs) that let you define and compose rich data model objects.
The following example shows how to create a simple data model, which you can later use to validate data (e.g. data received as body of an HTTP request):
import { withFields, string, number, boolean } from "@commodo/fields";
const Animal = withFields({
name: string({
validate: value => {
if (!value) {
throw Error("A pet must have a name!");
}
}
}),
age: number(),
isAwesome: boolean(),
about: fields({
value: {},
instanceOf: withFields({
type: string({ value: "cat" }),
dangerous: boolean({ value: true })
})()
})
})();
const animal = new Animal();
animal.populate({ age: "7" }); // Throws data type error, cannot populate a string with number.
animal.populate({ age: 7 });
await animal.validate(); // Throws a validation error - name must be defined.
animal.name = "Garfield";
await animal.validate(); // All good.
Using other HOFs, you can create more complex models, that have a name, attached hooks, and even storage layer, so that you can easily save the data to the database:
import { withFields, string, number, boolean, fields, onSet } from "@commodo/fields";
import { withName } from "@commodo/name";
import { withHooks } from "@commodo/hooks";
import { withStorage } from "@commodo/fields-storage";
import { MongoDbDriver, withId } from "@commodo/fields-storage-mongodb";
import { compose } from "ramda";
// Define User and Verification models.
const Verification = compose(
withFields({
verified: boolean(),
verifiedOn: string()
})
)();
const User = compose(
withFields({
firstName: string(),
lastName: string(),
email: compose(
onSet(value => value.toLowerCase())
)(string()),
age: number(),
scores: number({ list: true }),
enabled: boolean({ value: false }),
verification: fields({ instanceOf: Verification })
}),
withHooks({
async beforeCreate() {
if (await User.count({ query: { email: this.email } })) {
throw Error("User with same e-mail already exists.");
}
}
}),
withName("User"), // Utilized by storage layer, to determine collection / table name.
withId(),
withStorage({
driver: new MongoDbDriver({ database })
})
)();
const user = new User();
user.populate({
firstName: "Adrian",
lastName: "Smith",
email: "aDrIan@google.com",
enabled: true,
scores: [34, 66, 99],
verification: {
verified: true,
verifiedOn: "2019-01-01"
}
});
await user.save();
Fundamentally, Commodo is not an ORM/ODM, but can very quickly become one, by utilizing an additional HOF. You can use the already provided @commodo/fields-storage or even create your own if you don't like the existing one.
Using HOFs is a very flexible approach for defining your data models, because you can append only the functionality you actually need and will use.
The following section shows all of the useful higher order functions that you can use right now.
Package | Short Description | Version |
---|---|---|
@commodo/fields | The starting point of every model. Provides base string , number , boolean and model fields. |
|
@commodo/name | Assign a name to your models. | |
@commodo/hooks | Provides methods for defining and triggering hooks on your models. | |
@commodo/fields-storage | Enables saving models to a database (with an appropriate driver, e.g. MySQL). |
Package | Short Description | Version |
---|---|---|
@commodo/fields-storage-ref | Provides ref field, for saving references to other models saved in database. |
|
@commodo/fields-storage-mongodb | A MongoDB driver for @commodo/fields-storage package. | |
@commodo/fields-storage-soft-delete | Introduces deleted boolean field to mark whether a model was deleted or not, instead of physically deleting the entry in the storage. |
Package | Short Description | Version |
---|---|---|
commodo-fields-date | Provides date field, for saving dates. |
|
commodo-fields-object | Provides object field, for saving plain objects. |
|
commodo-fields-int | Provides int field, for saving integer numbers. |
|
commodo-fields-float | Provides float field, for saving float numbers. |
|
commodo-fields-storage-crud-logs | Adds and automatically manages createdOn , updatedOn , savedOn fields. |
Please see our Contributing Guideline which explains repo organization, linting, testing, and other steps.
This project is licensed under the terms of the MIT license.
Thanks goes to these wonderful people (emoji key):
Adrian Smijulj ๐ป ๐ ๐ก ๐ |
---|
This project follows the all-contributors specification. Contributions of any kind welcome!