server
or common
project sub-directory, depending on whether the model is server-only or defined for both server and client.
Overview
The LoopBack model generator creates a model JSON file for each model in either the server/models
or the common/models
directory (depending on whether the model is server-only or
defined on both server and client).
The file is named model-name.json
, where model-name
is the model name; for example, customer.json
.
The model JSON file defines models, relations between models, and access to models.
Note:
The LoopBack model generator automatically converts camel-case model names (for example MyModel)
to lowercase dashed names (my-model). For example, if you create a model named “FooBar” with the model generator, it creates files foo-bar.json
and foo-bar.js
in common/models
.
However, the model name (“FooBar”) will be preserved via the model’s name property.
For example, here is an excerpt from a model definition file for a customer model that would be in /common/models/customer.json
:
{
"name": "Customer", // See Top-level properties below
"description": "A Customer model representing our customers.",
"base": "User",
"idInjection": false,
"strict": true,
"options": { ... }, // See Options below - can also declare as top-level properties
"properties": { ... }, // See Properties below
"hidden": [...], // See Hidden properties below
"validations": [...], // See Validations below
"relations": {...}, // See Relations below
"acls": [...], // See ACLs below
"scopes": {...}, // See Scopes below
"indexes" : { ...}, // See Indexes below
"methods": [...], // See Methods below
"remoting": {
"normalizeHttpPath": true
},
"http": {"path": "/foo/mypath"}
}
Top-level properties
Properties are required unless otherwise designated.
Property | Type | Default | Description |
---|---|---|---|
acls | Array | N/A |
Set of ACL specifications that describes access control for the model.
See ACLs below.
|
base | String | None | Name of another model that this model extends. The model will "inherit" properties and methods of the base model. |
description | String or Array | None |
Optional description of the model. You can split long descriptions into arrays of strings (lines) to keep line lengths manageable; for example: [ "Lorem ipsum dolor sit amet, consectetur adipiscing elit", "sed do eiusmod tempor incididunt ut labore et dolore", |
excludeBaseProperties | Array | ['id', 'password'] |
Excludes the given list of properties from the base model from being visible. Use this instead of the approach documented under 'Exclude properties from base model' section below. |
forceId | Boolean | true |
If true, prevents clients from setting the auto-generated ID value manually. |
http.path | String | None | Customized HTTP path for REST endpoints of this model. |
strict | Boolean or String | false .If the data source is backed by a relational database, then default is true . |
Specifies whether the model accepts only predefined properties or not. One of:
|
idInjection | Boolean | true |
Whether to automatically add an id property to the model:
idInjection property in options (if present) takes precedence.
|
name | String | None | Name of the model. |
options | Object | N/A | JSON object that specifies model options. See Options below. |
plural | String | Plural of name property using standard English conventions. |
Plural form of the model name. |
properties | Object | N/A | JSON object that specifies the properties in the model. See Properties below. |
relations | Object | N/A | Object containing relation names and relation definitions. See Relations below. |
remoting. normalizeHttpPath |
Boolean | false |
If true , in HTTP paths, converts:
|
replaceOnPUT | Boolean | false |
If true, replaceOrCreate() and replaceById() use the HTTP PUT method; if false, updateOrCreate() and updateAttributes()/patchAttributes() use the HTTP PUT method. For more information, see Exposing models over REST. |
scopes | Object | N/A | See Scopes below. |
Options
The options
key specifies advanced options, for example data source-specific options.
Note:
You can set idInjection
here in options
or at the top-level. The value set here takes precedence over the top-level value of idInjection
.
Advanced options
Property | Type | Description |
---|---|---|
validateUpsert |
Boolean | By default, the upsert() (updateOrCreate() ) method does not enforce valid model data. Instead, it logs validation errors to the console. This preserves backwards compatibility with older 2.x versions. Set this property to true to ensure that upsert() returns an error when validation fails. The next major version of LoopBack will enable this option (set as true) by default.Set this property to false to prevent upsert() from calling any validators at all. By default, upsert() calls all validators and reports any validation errors to the console log. |
allowEternalTokens |
Boolean | Allow access tokens created with ttl = -1 never expire. |
Data source-specific options
When a model is attached a data source of certain type such as Oracle or MySQL,
you can specify the name of the database schema and table as properties under the key with the name of the connector type.
The value of this key must match the value of the corresponding connector
property in datasources.json.
For example, in the snippet below, there would be an entry in datasources.json
like this: "myDB": { "name": "myDB", "connector": "mysql", ... }
.
...
"options": {
"mysql": {
"table": "location"
},
"mongodb": {
"collection": "location"
},
"oracle": {
"schema": "BLACKPOOL",
"table": "LOCATION"
}
},
...
Properties
The properties key defines one or more properties, each of which is an object that has keys described in the following table. Below is an example a basic property definition:
...
"properties": {
"firstName": {
"type": "String",
"required": "true"
},
"id": {
"type": "Number",
"id": true,
"description": "User ID"
},
...
General property properties
Each model property can have the properties described in the following table. Only the type
property is required; for properties with only a type
, you can use the following shorthand:
"propertyName": "type"
For example:
...
"emailVerified": "boolean",
"status": "string",
...
Key | Required? | Type | Description |
---|---|---|---|
default | No | Any* |
Default value for the property. The type must match that specified by type .
|
applyDefaultOnWrites | No | Boolean | When set to `false`, does not write the default value to the database if the property value is missing. Defaults to `true`. |
persistDefaultValues | No | Boolean | When set to `false`, does not write the default value to the database if the property value matches the default value. Defaults to `true`. Applicable only for primitive data types. |
defaultFn | No | String |
A name of the function to call to set the default value for the property. Must be one of:
|
description | No | String or Array |
Documentation for the property.
You can split long descriptions into arrays of strings (lines) to keep line lengths manageable. For example:
[ "Lorem ipsum dolor sit amet, consectetur adipiscing elit", "sed do eiusmod tempor incididunt ut labore et dolore", "magna aliqua." ] |
doc | No | String | Documentation for the property. Deprecated, use "description" instead. |
id | No | Boolean |
Whether the property is a unique identifier. Default is false.
See Id property below.
|
generated | No | Boolean |
For auto-migrate usage. Only applicable for the id property. When it sets to true , the value of the property will be generated automatically with the default property type of the database. (e.g integer for MySQL and string for MongoDB)
|
index | No | Boolean | Whether the property represents a column (field) that is a database index. |
required | No | Boolean |
Whether a value for the property is required. If true, then adding or updating a model instance requires a value for the property. Default is false. |
type | Yes | String | Property type. Can be any type described in LoopBack types. |
useDefaultIdType | No | Boolean |
For auto-migrate usage. Set it to false when it's needed to auto-generate non-default type property values. For example, to use uuid as the id property, the id type should set to string, generated should be set to true, and this field should be set to false. Please check each connector's README file for more information about auto-migration/auto-update.
|
* | No | Any | See below. |
ID properties
A model representing data to be persisted in a database usually has one or more ID properties that uniquely identify the model instance.
For example, the user
model might have user IDs.
By default, if no ID properties are defined and the idInjection
property is true
(or is not set, since true
is the default),
LoopBack automatically adds an id
property to the model as follows:
id: {type: Number, generated: true, id: true}
The generated
property indicates the ID will be automatically generated by the database.
If true, the connector decides what type to use for the auto-generated key. For relational databases, such as Oracle or MySQL, it defaults to number
.
If your application generates unique IDs, set it to false.
To explicitly specify a property as ID, set the id
property of the option to true
. The id
property value must be one of:
true
: the property is an ID.false
(or any value that converts to false): the property is not an ID (default).- Positive number, such as 1 or 2: the property is the index of a composite ID.
In database terms, ID properties are primary key column(s). Such properties are defined with the ‘id’ attribute set to true or a number as the position for a composite key.
For example,
{
"myId": {
"type": "string",
"id": true
}
}
Then:
- If a model doesn’t have explicitly-defined ID properties, LoopBack automatically injects a property named “id” unless the
idInjection
option is set to false. - If an ID property has
generated
set to true, the connector decides what type to use for the auto-generated key. For example for SQL Server, it defaults tonumber
. - LoopBack CRUD methods expect the model to have an “id” property if the model is backed by a database.
- A model without any “id” properties can only be used without attaching to a database.
Composite IDs
LoopBack supports the definition of a composite ID that has more than one property. For example:
var InventoryDefinition = {
productId: { type: String, id: 1 },
locationId: { type: String, id: 2 },
qty: Number
};
The composite ID is (productId, locationId) for an inventory model.
Important:
Composite IDs are not currently supported as query parameters in REST APIs and DAO methods like findOrCreate
, updateOrcreate
and replaceOrCreate
.
Data mapping properties
When using a relational database data source, you can specify the following properties that describe the columns in the database.
Property | Type | Description |
---|---|---|
columnName | String | Column name |
dataType | String | Data type as defined in the database |
dataLength | Number | Data length |
dataPrecision | Number | Numeric data precision |
dataScale | Number | Numeric data scale |
nullable | Boolean | If true, data can be null |
For example, to map a property to a column in an Oracle database table, use the following:
...
"name": {
"type": "String",
"required": false,
"length": 40,
"oracle": {
"columnName": "NAME",
"dataType": "VARCHAR2",
"dataLength": 40,
"nullable": "Y"
}
}
...
Non-public Information
Removed until https://github.com/strongloop/loopback-datasource-juggler/issues/128 is resolved.</div>
Conversion and formatting properties
Format conversions are declared in properties, as described in the following table:
<table>
<tbody>
<tr>
<th>Key</th>
<th>Type</th>
<th>Description</th>
</tr>
<tr>
<td>trim</td>
<td>Boolean</td>
<td>Whether to trim the string</td>
</tr>
<tr>
<td>lowercase</td>
<td>Boolean</td>
<td>Whether to convert a string to lowercase</td>
</tr>
<tr>
<td>uppercase</td>
<td>Boolean</td>
<td>Whether to convert a string to uppercase</td>
</tr>
<tr>
<td>format</td>
<td>Regular expression</td>
<td>Format for a date property.</td>
</tr>
</tbody>
</table>
</div>
Exclude properties from base model
By default, a model inherits all properties from the base. To exclude some base properties from being visible, you need to set excludeBaseProperties = ['property-be-excluded-from-base-model']
.
excludeBaseProperties
is recommended approach over previous approach of setting the base property to ‘null’ or ‘false’.
For example,
...
"base": "User",
"excludeBaseProperties" = ["lastUpdated", "credentials", "challenges"],
"properties": {
...
}
...
Another example,
Excludes ‘id’ property from the base model, “Model”
...
"base": "Model",
"excludeBaseProperties" = ["id"],
"properties": {
...
}
...
Below way of excluding base properties by setting the base property to ‘null’ or ‘false’ is not recommended. Instead, use excludeBaseProperties
as shown above.
...
"base": "User",
"properties": {
"lastUpdated": false,
"credentials": null,
"challenges": null,
"modified": "date"
}
...
Hidden properties
A hidden property is not sent in the JSON data in the application’s HTTP response. The property value is an array of strings, and each string in the array must match a property name defined for the model.
An example of a hidden property is User.password
:
{
...
"properties": {
...
"password": {
"type": "string",
"required": true
},
...
},
"hidden": ["password", "verificationToken"],
...
}
If you want to white-list the fields returned instead of black-listing them, consider:
- Applying the
fields
of the model’s defaultscope
. This will operate at the database response layer so limiting your ability to check a field in the database that you otherwise would not wish exposed to the outside world (a private flag, for example). - Overriding your model’s
toJSON
method
See discussion of white-listing on GitHub.
Protected properties
The protected
property is an array of strings, and each string in the array must match a property name defined for the model.
A protected property is not sent in HTTP response JSON data if the object is nested inside another object. For instance, suppose there is an Author object and a Book object. Book has a relation to Author, and Book is a public API. The Author model has personal information (such as social security number) which should be “protected” so anyone looking up the author of the book will not get that information.
Tip: Hidden and protected properties are subtly different.
hidden
determines whether a property is returned by a query directly against the model that contains the property.protected
determines whether a property is returned by a query against a model that has a relation to the model being queried.
An example configuring email
as a protected property:
{
...
"properties": {
...
"email": {
"type": "string",
"required": true
},
...
},
"protected": ["email"],
...
}
Validations
Warning: This is not yet implemented. You must currently validate in code; see Validating model data.
Specify constraints on data with validations
properties. See also Validatable class.
Key | Type | Description |
---|---|---|
default | Any | Default value of the property. |
required | Boolean | Whether the property is required. |
pattern | String | Regular expression pattern that a string should match |
max | Number | Maximum length for string types. |
min | Number | Minimum length for string types. |
length | Number | Maximum size of a specific type, for example for CHAR types. |
For example:
"username": {
"type": "string",
"description": "User account name",
"min": 6,
"max": 24
}
Relations
The relations
key defines relationships between models through a JSON object.
Each key in this object is the name of a related model, and the value is a JSON object as described in the table below.
For example:
...
"relations": {
"accessTokens": {
"model": "accessToken",
"type": "hasMany",
"foreignKey": "userId"
},
"account": {
"model": "account",
"type": "belongsTo"
},
"transactions": {
"model": "transaction",
"type": "hasMany"
}
},
...
Key | Type | Description |
---|---|---|
foreignKey | String | Optional foreign key used to find related model instances. |
keyThrough | String | Foreign key to be used in a HasMany relation. |
model | String | Name of the related model. Required. |
type | String |
Relation type. Required. See Creating model relations for more information. One of:
For hasMany, you can also specify a hasManyThrough relation by adding a "through" key:
See example below. |
through | String | Name of model creating hasManyThrough relation. See example below. |
options.disableInclude | Boolean | Does not fetch the data if the relation is used in an include statement |
options.http.path | string | Set the relation http path |
Example of hasManyThrough:
"patient": {
"model": "physician",
"type": "hasMany",
"through" : "appointment"
}
ACLs
The value of the acls
key is an array of objects that describes the access controls for the model. Each object has the keys described in the table below.
"acls": [
{
"permission": "ALLOW",
"principalType": "ROLE",
"principalId": "$everyone",
"property": "myMethod"
},
...
]
Key | Type | Description |
---|---|---|
accessType | String |
The type of access to apply. One of:
|
permission | String |
Type of permission granted. Required.
One of:
|
principalId | String |
Principal identifier. Required.
The value must be one of:
|
principalType | String |
Type of the principal. Required.
One of:
|
property | String Array of Strings |
Specifies a property/method/relation on a given model. It further constrains where the ACL applies.
Can be:
|
Scopes
Scopes enable you to specify commonly-used queries that you can reference as method calls on a model.
The scopes
key defines one or more scopes (named queries) for models.
A scope maps a name to a predefined filter object to be used by the model’s find()
method; for example:
"scopes": {
"vips": {"where": {"vip": true}},
"top5": {"limit": 5, "order": "age"}
}
The snippet above defines two named queries for the model:
- vips: Find all model instances with vip flag set to true
- top5: Find top five model instances ordered by age
Within the scopes object, the keys are the names, and each value defines a filter object for PersistedModel.find().
You can also define a scope programmatically using a model’s scope()
method, for example:
User.scope("vips", { where: { vip: true } });
User.scope("top5", { limit: 5, order: "age" });
Now you can call the methods defined by the scopes; for example:
User.vips(function(err, vips) {
//...
});
Default scope
If you wish for a scope to be applied across all queries to the model, you can use the default scope for the model itself.
For example:
{
"name": "Product",
"properties": {
...
},
"scope": {
"order": "name",
"limit": 100,
"where": {
"deleted": false
}
}
}
Now, any CRUD operation with a query parameter runs in the default scope will be applied; for example, assuming the above scope, a find operation such as
Product.find({ offset: 0 }, cb);
Becomes the equivalent of this:
Product.find(
{ order: "name", offset: 0, limit: 100, where: { deleted: false } },
cb
);
Default scopes with where filters
Adding a scope
to a model definition (in the model.json file) automatically adds a method to model called defaultScope()
.
LoopBack will call this method whenever a model is created, updated, or queried.
Tip: Default scopes with a where
filter may not work as you expect!
Each time a model instance is created or updated, the generated defaultScope()
method will modify the model’s properties matching the where
filter to enforce the values specified.
If you don’t want to have the default scope applied in this way, use named scopes where possible.
If you must use a default scope, but don’t want it to affect upsert()
, for example, simply override the model’s defaultScope()
method prior to calling upsert()
; for example:
var defaultScope = Report.defaultScope;
Report.defaultScope = function(){};
Report.upsert({id: reportId, 'deleted': true}, function(...) {
Report.defaultScope = defaultScope;
//...
});
Methods
The methods
key defines remote methods for the model.
Its value is an object with a string key for each remote method name:
- Instance method names must start with
prototype.
. - Static method names can be any legal name.
For example, the following defines a static remote method called “greet” and an instance method called “getProfile”.
...
"methods": {
"greet": {
"accepts": [
{
"arg": "msg",
"type": "string",
"http": {
"source": "query"
}
}
],
"returns": {
"arg": "greeting",
"type": "string"
},
"http": {
"verb": "get"
}
},
"prototype.getProfile": {
... // Instance remote method - options
}
...
Remote method options
You specify remote method options when you register a remote method, either as an argument to the Model.remoteMethod()
method if you register it in code, or in the methods
key if you register it in JSON. Either way, it’s a JavaScript object with the same set of properties.
Important:
All of the options properties are optional. However, if the remote method requires arguments, you must specify accepts
; if the remote method returns a value, you must specify returns
.
Option | Description | Example |
---|---|---|
accepts |
Defines arguments that the remote method accepts that map to the static method you define. For the example above, the function signature is Person.greet(name, age, callback)...so name is the first argument, age is the second argument and callback is automatically provided by LoopBack (do not specify it in your accepts array). For more information, see Argument descriptions.The default value is the empty array, [ ] .
|
{ ... accepts: [ {arg: 'name', type: 'string'}, {arg: 'age', type: 'number'}, ...], ... } |
accessScopes |
Defines access scopes. A user will be allowed to invoke this remote
method only when their access token was granted at least one of
the scopes defined by `accessScopes` list. See also
Authorization scopes.
The default value is a list with a single scope DEFAULT .
|
accessScopes: [ 'read', 'read:user' ] |
description | Text description of the method, used by API documentation generators such as OpenAPI (formerly Swagger). You can put long strings in an array if needed (see note below). | |
http | Specifies information about the route at which the method is exposed. See http property below. | |
notes | Additional notes, used by OpenAPI (formerly Swagger). You can put long strings in an array if needed (see note below). | |
documented |
If set to false , this method will not be present in generated OpenAPI (formerly Swagger) documentation.
|
|
returns |
Describes the remote method's callback arguments; See Argument descriptions. The err argument is assumed; do not specify.
Default if not provided is the empty array, [] .
|
returns: {arg: 'greeting', type: 'string'} |
Tip: You can split long strings in the description
and notes
options into arrays of strings (lines) to keep line lengths manageable. For example:
[
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,",
"sed do eiusmod tempor incididunt ut labore et dolore",
"magna aliqua."
]
http property
The http
property provides information on HTTP route at which the remote
method is exposed.
Argument descriptions
The accepts
and returns
options properties define either a single argument as an object or an ordered set of arguments as an array.
The following table describes the properties of each individual argument.
Property (key) | Type | Description |
---|---|---|
arg | String | Argument name |
description | String or Array | A text description of the argument. This is used by API documentation generators like OpenAPI (formerly Swagger). You can put long strings in an array if needed (see note above). |
http | Object or Function | For input arguments: a function or an object describing mapping from HTTP request to the argument value. See HTTP mapping of input arguments below. |
http.target | String |
Map the callback argument value to the HTTP response object. The following values are supported.
|
required | Boolean | True if argument is required; false otherwise. |
root | Boolean | For callback arguments: set this property to true
if your function has a single callback argument to use as the root object returned to remote caller.
Otherwise the root object returned is a map (argument-name to argument-value).
|
type | String | Argument datatype; must be a Loopback type. Additionally, callback arguments allow a special type "file"; see below. |
default | String | Default value that will be used to populate loopback-explorer input fields and swagger documentation. Note: This value will not be passed into remote methods function if argument is not present. |
documented | Boolean | If set to false , this parameter will not be present in generated OpenAPI (formerly Swagger) documentation. |
For example, a single argument, specified as an object:
{arg: 'myArg', type: 'number'}
Multiple arguments, specified as an array:
[
{arg: 'arg1', type: 'number', required: true},
{arg: 'arg2', type: 'array'}
]
Returning a file (stream) response
Specify { type: 'file', root: true }
for a callback argument that will be sent directly as the response body. A file argument can be set to one of the following values:
- String
- Buffer
- ReadableStream (anything that exposes
.pipe()
method)
Example:
module.exports = function(MyModel) {
MyModel.download = function(cb) {
// getTheStreamBody() can be implemented by calling http.request() or fs.readFile() for example
getTheStreamBody(function(err, stream) {
if (err) return cb(err);
// stream can be any of: string, buffer, ReadableStream (e.g. http.IncomingMessage)
cb(null, stream, 'application/octet-stream');
});
};
MyModel.remoteMethod('download', {
returns: [
{arg: 'body', type: 'file', root: true},
{arg: 'Content-Type', type: 'string', http: { target: 'header' }}
]
});
};
HTTP mapping of input arguments
There are two ways to specify HTTP mapping for input parameters (what the method accepts):
- Provide an object with a
source
property - Specify a custom mapping function
Using an object with a source property
To use the first way to specify HTTP mapping for input parameters, provide an object with a source
property that has one of the values shown in the following table.
Value of source property | Description |
---|---|
body | The whole request body is used as the value. |
form |
The value is looked up using req.body which searches the request body
|
query |
The value is looked up using req.query which searches the query string
|
path |
The value is looked up using req.params which searches the route arguments
|
req | The Express HTTP request object. |
res | The Express HTTP response object. |
context | The whole context object, which holds request and response objects. |
For example, an argument getting the whole request body as the value:
{ arg: 'data', type: 'object', http: { source: 'body' } }
Another example showing the Express HTTP request and response objects:
[
{arg: 'req', type: 'object', 'http': {source: 'req'}},
{arg: 'res', type: 'object', 'http': {source: 'res'}}
]
Using a custom mapping function
The second way to specify HTTP mapping for input parameters is to specify a custom mapping function; for example:
{
arg: 'custom',
type: 'number',
http: function(ctx) {
// ctx is LoopBack Context object
// 1\. Get the HTTP request object as provided by Express
var req = ctx.req;
// 2\. Get 'a' and 'b' from query string or form data and return their sum.
return -req.param('a') - req.param('b');
}
}
If you don’t specify a mapping, LoopBack will determine the value as follows (assuming name
as the name of the input parameter to resolve):
- If there is an HTTP request parameter
args
with JSON content, then it uses the value ofargs['name']
. - Otherwise, it uses
req.param('name')
.
Returning data outside of a JSON field
Specifying a return argument with the arg property will automatically return a JSON object with your data stored within a field of the same name.
If you want to return data as the main response, for example an array, you can do so by setting the root property within the returns object and omitting arg.
returns: {type: 'array', root: true}
Indexes
Warning: Indexes will not be automatically created for you, even with NoSQL datasource connectors like MongoDB. You must run automigrate or autoupdate to create your indexes!
Declare indexes for a model with the indexes
property, for example:
"indexes": {
"name_age_index": {
"keys": {"name": 1, "age": -1}
},
"age_index": {"age": -1}
}
The snippet above creates two indexes for the declaring model:
- A composite index named
name_age_index
with two keys:name
in ascending order andage
in descending order. - A simple index named
age_index
with one key:age
in descending order.
The full syntax for an individual index is:
"<indexName>": {
"keys": {
"<key1>": 1,
"<key2>": -1
},
"options": {
"unique": true
}
}
Note: A key value of 1 specifies ascending order, and -1 specifies descending order.
If you don’t need to specify any options, you can use a shortened form:
"<indexName>": {
"<key1>": 1,
"<key2>": -1
}
You can specify indexes at the model property level too, for example:
{
"name": {
"type": "String",
"index": true
},
"email": {
"type": "String",
"index": {
"unique": true
}
},
"age": "Number"
}
This example creates two indexes: one for the name
key and another one for the email
key. The email
index is unique.
MySQL indexes
For MySQL, you can declare multi-column indexes as follows (for example):
...
"indexes":
{
"UNIQUE_INDEX": {
"columns": "`column1`,`column2`,...",
"kind": "unique"
}
},
...