# 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);