Posted by & filed under Javascript, Node.js, Work.

This is about a WIP project of making an ORM for Node.js with pluggable adapters, with code name “Prometheus”:

https://github.com/shubik/prometheus

The idea was to make a simple ORM with a fairly standard API (get, set, save, destroy, etc.) with adapters for different databases which pretty much offer CRUD and a couple extra convenience methods.

We need a way to describe models using model-specific schemas and optional model-specific class or static methods and mix-ins (which are also applied to model constructor’s prototype), as well as optional hooks used during the model’s lifecycle.

In order to have this, we have a generic model (or model factory) which takes model-specific options and returns a constructor function used to instantiate a model. Model factory creates a generic constructor and augments it with model-specific options (e.g. schema, store, mixins, static methods, prototype methods and hooks). Pretty straightforward so far.

Now a model constructor that we get uses deferred/promise pattern where we expect asynchronousity. The debate that I had was between hiding promises inside the model and having each method execute only once model has been initialized (e.g. created new or loaded from database), but I did not feel that this would be a consistent pattern.

I still decided to use an internal “ready” promise but it’s not used by all methods – only by async methods such as save() or destroy().  Here’s a gist from the generic model constructor:

Generic Model has an internal load() method which is called if we provide model ID in constructor arguments. If load() successfully fetches our model from the database, this._loading is resolved with this or the model itself, otherwise it’s resolved with null.

What is important is that model constructor itself returns us a promise, or this internal this._ready property, which, as we remember, is resolved with an actual model. Therefore every time we initiate a model, we add deferred style callbacks for success and fail. Success callback has model as argument, and error callback has error as argument.

So, instantiating a model and doing things with a model looks like this:

Above example is for creating a new model, because as you can see we do not provide model id to the constructor function. Example below will do the same for an existing model:

Using same reasoning, model’s instance methods which are async do the same thing as model constructor: they return a promise which is resolved with model (or this) once database action succeeds or fails. It is not necessary to add any callbacks to these methods — you can use model.save(), model.destroy() without callbacks. But in practice we will not send response to client unless we have a confirmation or error result from these methods.

I will continue to write posts about development of this ORM when there is reasonable progress. In the mean time you can check out my Gitgub repo at https://github.com/shubik/prometheus.

Leave a Reply

  • (will not be published)