This library is under developments, so please help improving by testing and submitting issues. Many thanks
Vitamin provides a simple and easy to use Data Mapper implementation to work with your relational database.
Based on knex, it supports Postgres, MySQL, MariaDB, SQLite3, and Oracle databases, featuring both promise based and traditional callback interfaces, providing lazy and eager relationships loading, and support for one-to-one, one-to-many, and many-to-many relations.
Installation
$ npm install --save vitamin # Then add one of the supported database drivers $ npm install pg$ npm install mysql$ npm install mysql2$ npm install oracle$ npm install sqlite3$ npm install mariasql$ npm install strong-oracle
Vitamin is initialized by passing an initialized Knex client instance. The knex documentation provides a number of examples for different use cases.
// for example the database configuration is located at "app/config/database.js"var knex = client: 'mysql' connection: host : '127.0.0.1' user : 'your_database_user' password : 'your_database_password' database : 'your_database_name' charset : 'utf8' // exports'vitamin'knex
Defining models
To get started, let's define a user model by specifying both, the primaryKey
name, and the tableName
// using the previous initialized vitamin object,// we define a model called `user` using `model` method of vitamin object
vitamin.model()
also accepts a custom mapper instance passed as a second argument instead of a config object.
CRUD operations
You can use the standard Node.js style callbacks by calling
.asCallback(function (error, result) {})
on any promise method
Create
To create a new record in the database, simply create a new model instance, set its attributes, then call the save
method
// access the model defined earliervar User = vitamin // we create a new instance of User model with `make()`var user = User// or simply with the new operatorvar user = name: "John" occupation: "Developer" // then we save ituser // or using the callbacksuser
Another shorthand to create and save a new user is the static method create()
User
Read
Below a few examples of different data access methods provided by vitamin
- Retrieving multiple models
// get a collection of all usersUser
The fetch()
method will return all the rows in the users
table as a collection of User
models.
If you may also add constraints to queries, you can use the where()
methods in the query builder
object returned by query()
User
The findMany()
query method return a collection of models by their primary keys
User
- Retrieving single model
Of course, in addition to retrieve all records of a given table, you may also retrieve a single record using find()
or first()
Instead of returning a collection of models, these methods return only a single model instance
// find a user by its primary keyUser // fetch the `id` and `email` of the first admin userUser
findOrFail()
,findOrNew()
,firstOrCreate()
,firstOrNew()
,firstOrFail()
query methods are also available
findOrFail()
andfirstOrFail()
throw aModelNotFoundError
if no result found
Update
The save()
model method may be used to update a single model that already exists in the database.
To update a model, you should retrieve it, change any attributes you wish to update, and then call the save()
// post model is retrieved from `posts` table// then we modify the status attribute and save itvar Post = vitamin Post
In case you have many attributes to edit, you may use the update()
method directly
var data = 'status': "published" 'published_at': Post
Delete
Likewise, once retrieved, a model can be destroyed which removes it from the database.
To delete a model, call destroy()
on an existing model instance
Post
Of course, you may also run a delete query on a set of models.
// we will delete all posts that are marked as draftPost
Events
Model events allow you to attach code to certain events in the lifecycle of yours models.
This enables you to add behaviors to your models when those built-in events creating
, created
, saving
, saved
, updating
, updated
, deleting
or deleted
occur.
Events can be defined when you register the model using the events
config property
vitamin
Or, later with the static method on()
// attach a listener for `created` eventUser // Events `saving - creating - created - saved` are fired in order when we create a new model>>> User
You can also attach the same handler for many events separated by a white space
Post
The built-in events are fired automatically by the mapper, but you can trigger manually those events, or any custom ones with emit()
Post orderModel
Collections
All multi-result methods, like fetch()
or findMany()
, return instances of the Collection
class instead of simple arrays.
However, collections are much more powerful than arrays and expose a variety of operations, including saving of emiting events, that may be chained using an intuitive interface.
// retrieve the draft posts and make a delete query to remove those without 'publish_date'Post
Associations
Vitamin makes managing and working with relationships easy, and supports several types of relations:
- One To One
- One To Many
- Many To Many
- Polymorphic relations
Defining relations
One to One
Let's define a relation one to one between Person
and Phone
.
var Phone = vitamin var Person = vitamin
One To Many
An example for this type, is the relation between blog Post
and its Author
var User = vitamin var Post = vitamin
Many To Many
This relation is more complicated than the previous.
An example of that is the relation between Product
and Category
, when a product has many categories, and the same category is assigned to many products.
A pivot table product_categories
is used and contains the relative keys product_id
and category_id
vitamin vitamin
Polymorphic relations
Polymorphic relations allow a model to belong to more than one other model on a single association.
For example, the users of the application can like both comments and posts.
Using polymorphic relationships, you can use a single Like
model for the both scenarios.
Here is the schema of this example database:
posts (id, title, body)
comments (id, post_id, body)
likes (id, likeable_id, likeable_type)
The likeable_id
column will contain the ID of the post or the comment, while the likeable_type
will contain the name of the owning model.
var Post = vitamin var comment = vitamin var Like = vitamin
In addition to those associations, you can also define many-to-many polymorphic relations.
For example, a Post
and Video
models could share both a relation to Tag
model.
Using a polymorphic many-to-many relation, you can use a single list of unique tags that are shared across blog posts and videos, or any other model.
tags (id, name)
posts (id, title, body)
videos (id, name, filename)
taggables (tag_id, taggable_id, taggable_type)
var Post = vitamin // `taggables` is the pivot table name.// `taggable` will be used as a prefix for morph columns `taggable_id` and `taggable_type` var Tag = vitamin
Querying relations
Lazy loading
We will use the relations defined below, to lazy load the related models
// load the related phone model of the person with the id 123// we access the relation via `phone()` which return a HasOne relation instancevar person = Person person
Eager loading
To load a model and its relationships in one call, you can use the query method withRelated
// fetch the first article and its authorPost // load all authors with their postsUser
Saving related models
Instead of manually setting the foreign keys, Vitamin provides many methods to save the related models.
create()
and createMany()
In addition to the save
and saveMany
methods, you may also use the create
method,
which accepts an array of attributes, creates a model, and inserts it into the database.
// create and attach a post commentpost // create and attach the post commentspost
save()
and saveMany()
var comment = Comment // saving and attach one commentpost // saving many eventspost
associate()
and dissociate()
When updating a belongsTo
or morphTo
relationship, you may use the associate
method.
var john = Person // set the foreign key `owner_id` and save the phone modelphone // unset the foreign key, then savelike
attach()
, detach()
and updatePivot()
When working with many-to-many relationships, Vitamin provides a few additional helper methods to make working with related models more convenient.
// to attach a role to a user by inserting a record in the joining tableuser
To remove a many-to-many relationship record, use the detach method.
// detach all roles of the loaded useruser // detach only the role with the given iduser // detach all roles with the given idsuser
If you need to update an existing row in your pivot table, you may use updatePivot
method
user
sync()
You may also use the sync
method to construct many-to-many associations.
The sync method accepts an array of IDs to place on the intermediate table.
Any IDs that are not in the given array will be removed from the intermediate table.
So, after this operation is complete, only the IDs in the array will exist in the intermediate table:
// using callbacksuser // using promisesuser
You may also pass additional intermediate table values with the IDs:
user
toggle
This method allows you toggle relationships without pain.
It accepts a model, an ID or an array of IDs, and sync only those ids without affecting the others.
Useful for likes
, stars
or following
relationships
// grant or revoke the admin role to a specific useruser // add or remove a like on a projectuser