loopback-datasource-juggler/docs/ldl.md

269 lines
7.8 KiB
Markdown
Raw Normal View History

2013-08-16 17:53:33 +00:00
# LoopBack Definition Language (LDL)
LoopBack Definition Language (LDL) is simple DSL to define data models in JavaScript or plain JSON. With LoopBack, we often
start with a model definition which describes the structure and types of data. The model establishes common knowledge of
data in LoopBack.
## Describing a model
Let's start with a simple example in plain JSON.
{
"id": "number",
"firstName": "string",
"lastName": "string"
}
The model simply defines a `user` model that consists of three properties:
* id - The user id. It's a number.
* firstName - The first name. It's a string.
* lastName - The last name. It's a string.
Each key in the JSON object defines a property in our model which will be cast to its associated type. The simplest form of
a property definition is `propertyName: type`. The key is the name of the property and the value is the type of the property.
We'll cover more advanced form later in this guide.
LDL supports a list of built-in types, including the basic types from JSON:
* String
* Number
* Boolean
* Array
* Object
**Note**: The type name is case-insensitive, i.e., either "Number" or "number" can be used.
The same model can also be described in JavaScript code:
var UserDefinition = {
id: Number,
firstName: String,
lastName: String
}
As we can see, the JavaScript version is less verbose as it doesn't require quotes for property names. The types are
described using JavaScript constructors, for example, `Number` for `"Number"`. String literals are also supported.
Now we have the definition of a model, how do we use it in LoopBack Node.js code? It's easy, LoopBack will build a
JavaScript constructor (or class) for you.
## Creating a model constructor
LDL compiles the model definition into a JavaScript constructor using `ModelBuilder.define` APIs. ModelBuilder is the
basic factory to create model constructors.
ModelBuilder.define() method takes the following arguments:
- name: The model name
- properties: An object of property definitions
- options: An object of options, optional
var ModelBuilder = require('loopback-datasource-juggler').ModelBuilder;
// Create an instance of the ModelBuilder
var modelBuilder = new ModelBuilder();
// Describe the user model
var UserDefinition = {
id: Number,
firstName: String,
lastName: String
}
// Compile the user model definition into a JavaScript constructor
var User = modelBuilder.define('User', UserDefinition);
// Create a new instance of User
var user = new User({id: 1, firstName: 'John', lastName: 'Smith'});
console.log(user.id); // 1
console.log(user.firstName); // 'John'
console.log(user.lastName); // 'Smith'
That's it. Now you have a User constructor representing the user model.
At this point, the constructor only has a set of accessors to model properties. No behaviors have been introduced yet.
## Adding methods to a model constructor
There are a few ways to add methods to a model constructor:
1. Create the model constructor from a data source
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
// Compile the user model definition into a JavaScript constructor
var User = ds.define('User', UserDefinition);
// Create a new instance of User
User.create({id: 1, firstName: 'John', lastName: 'Smith'}, function(err, user) {
console.log(user); // The newly created user instance
User.findById(1, function(err, user) {
console.log(user); // The user instance for id 1
user.firstName = 'John1'; // Change the property
user.save(function(err, user) {
console.log(user); // The modified user instance for id 1
});
};
});
2. Attach the model to a data source
A plain model constructor created from `ModelBuilder` can be attached a `DataSource`.
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
User.attachTo(ds); // The CRUD methods will be mixed into the User constructor
3. Manually declare methods to the model constructor
We can add static and prototype methods to a model constructor.
// Define a static method
User.greet = function(msg) {
console.log('Hello ', msg);
};
// Define a prototype method
User.prototype.getFullName = function () {
return this.firstName + ' ' + this.lastName;
};
User.greet('world'); // prints 'Hello world'
var user = new User({id: 1, firstName: 'John', lastName: 'Smith'});
console.log(user.getFullName()); // 'John Smith'
## Exploring advanced features
The basic example use `propertyName: type` to describe a property.
Properties can have options in addition to the type. LDL uses a JSON object to describe such properties, for example:
"id": {"type": "number", "id": true, "doc": "User ID"}
"firstName": {"type": "string", "required": true, "oracle": {"column": "FIRST_NAME", "type": "VARCHAR(32)"}}
Common options for a property are:
* type: The property type
- String/Text
- Number
- Date
- Boolean
- Buffer/Binary
- Array
- Any/Object/JSON
- GeoPoint
* id: Indicate if the property is an `id` of the model. The value can be true, false, or a number
- true: It's an id
- false: It's not an id
- 0: It's not an id
- 1: It's the first part of the composite id
LDL supports the definition of a composite id that has more than one properties. 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.
* doc: Documentation of the property
* default: The default value of the property
Constraints are modeled as options, for example:
* required: Indicate if the property is required
* pattern: A regular expression pattern that a string should match
* min/max: The minimal and maximal value
* length: The maximal length of a string
Format conversions can also be declared as options, for example:
* trim: Trim the string
* lowercase: Convert the string to be lowercase
* uppercase: Convert the string to be uppercase
* format: Format a Date
Data source specific mappings can be added to the property options, for example, to map a property to be a column in
Oracle database table, you can use the following syntax:
"oracle": {"column": "FIRST_NAME", "type": "VARCHAR", "length": 32}
### Array types
LDL supports array types as follows:
{emails: [String]}
or
{"emails": ["String"]}
or
{emails: [{type: String, length: 64}]}
### Object types
#### Embed anonymous types
#### Reference named types
### Advanced example
var User = modelBuilder.define('User', {
name: String,
bio: ModelBuilder.Text,
approved: Boolean,
joinedAt: Date,
age: Number,
address: {
street: String,
city: String,
state: String,
zipCode: String,
country: String
},
emails: [{
label: String,
email: String
}],
friends: [String]
});
### Model level options
LDL uses a list of options to control the definition of a model.
- strict:
- idInjection:
- Data source specific mappings
### Relations between models
// setup relationships
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
User.hasAndBelongsToMany('groups');
### Extend from a base model
### Mix in model definitions
var Group = modelBuilder.define('Group', {group: String});
User.mixin(Group);