I wish to re-use the .json - Files of the models and share them between different services. The services have very different concerns but want to operate on the same data structures. In order to avoid duplication of effort and errors, I would like to put the .json - Files into a package and associate the Javascript - Files with the model files separately in each project. Example:
A directory only for the .json definitions that can be shared among various applications accessing the same data source:
models
- ModelA.json
- ModelB.json
- ...
Per application a directory with the application specific logic:
model-logic
- ModelA.js
- ModelB.js
- ...
If I try this with loopback, the model files can be found, but unless the Javascript files are in the same directory, they are not associated with the models. Is there a way this can be accomplished?
Disclaimer: I am a maintainer of LoopBack and the author of loopback-boot module.
In LoopBack, we have a set of APIs for registering new models, and then a conventional bootstrapper (the loopback-boot module) that defines a convention for project layout and calls LoopBack APIs to automatically load and define model from JSON and JS files. As you have correctly observed, loopback-boot assumes both JS and JSON files are in the same location.
To support the case where each application wants to define custom model methods, few solutions come to my mind.
You can leverage model inheritance:
name
and base
model name to inherit from, plus perhaps remoting metadata for your custom methods).Package containing shared models:
- BaseModelA.json
- (empty BaseModelA.js that can be omitted)
- BaseModelB.json
- (empty BaseModelB.js that can be omitted)
- ...
In your app, let's create ModelA inheriting from ModelB and adding additional behavior:
common/models/ModelA.json
{
"name": "ModelA",
"base": "BaseModelA",
}
common/models/ModelA.js
'use strict';
module.exports = function(ModelA) {
// place your code here
}
The missing piece is to tell loopback-boot to look for models in your shared package. Assuming your package is called "models", contains the models in common/models
directory and gets installed to node_modules/models
, you need to edit your server/model-config.json
as follows:
{
"_meta": {
"sources": [
"loopback/common/models",
"loopback/server/models",
"models/common/models", // <-- ADD THIS LINE
"../common/models",
"./models"
],
// etc.
},
// etc.
}
Alternatively, you don't have to use loopback-boot to setup your application runtime, you can write your own conventional bootstrapper.
Here are the APIs to use:
Data sources are defined via app.dataSource(name, config)
(apidocs). To define datasources, loopback-boot essentially loads datasources.json
and iterates through all top-level properties, calling app.dataSource(key, data[key])
.
Models are loaded in multiple steps:
If you are using mixins, then you need load them first. The API is not surfaced at app
level, you have to call app.registry.modelBuilder.mixins.define(fn)
(source).
A model needs to be defined first by calling app.registry.createModel(config)
, where config
argument is set to the content of a model JSON file.
Once you have the model constructor, you need to attach it to a datasource and expose it via the REST API by calling app.model(ModelCtor, config)
. The config
argument contains model configuration from server/model-config.json
file, for example {dataSource: 'db', public: true}
.
To load the remaining artifacts (app config, middleware config, components, etc.), it may be best to keep using loopback-boot. Just tell the bootstrapper there are no models nor datasources to load.
const app = loopback();
// 1. load your datasources and models
// (TODO)
// 2. use loopback-boot to do the rest
boot(
app,
{
appRootDir: __dirname,
models: {},
dataSources: {},
modelSources: [],
mixinSources: [],
});
// etc.
(The example above is for loopback-boot@2.x
, I am not familiar with the configuration options of the new version loopback-boot@3.x
.)