This is about a WIP project of making an ORM for Node.js with pluggable adapters, with code name “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
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
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.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.