Chinese Documentation : Tutorial: model relations

Follow this tutorial to create a web application (loopback-example-relation) that demonstrates LoopBack model relations. The application's main page consists of links to query and filter data through an exposed REST API. 

Create application

Begin by scaffolding the application with slc loopback:

$ slc loopback

You'll see:

...
[?] Enter a directory name where to create the project: (.)

Enter loopback-example-relation as the project name (referred to as the project root henceforth). Finish the creation process by following the prompts.

Create models

You'll use an in-memory database to hold data. Create a model named Customer as follows:

$ cd loopback-example-relation
$ slc loopback:model Customer

You'll see:

[?] Enter the model name: Customer
[?] Select the data-source to attach Customer to: db (memory)
[?] Expose Customer via the REST API? Yes
[?] Custom plural form (used to build REST URL):
Let's add some Customer properties now.

Enter an empty property name when done.
[?] Property name: name
   invoke   loopback:property
[?] Property type: string
[?] Required? No

Let's add another Customer property.
Enter an empty property name when done.
[?] Property name: age
   invoke   loopback:property
[?] Property type: number
[?] Required? No

Let's add another Customer property.
Enter an empty property name when done.
[?] Property name: #leave blank, press enter

Follow the prompts to finish creating the model. Repeat for Review and Order using the following properties:

  • Review
    • product: String
    • star: Number
  • Order
    • description: String
    • total: Number
Icon

You'll see new files customer.json, order.json, and review.json in /common/models when you're done.

Create the front-end

Now create a front-end to make it easier to analyze the data.  Install Embedded JavaScript (EJS), by running the following command from the project root:

$ npm install --save ejs

Then configure the application view engine by modifying server/server.js as follows:

...
// -- Mount static files here--
...
app.set('view engine', 'html');
app.engine('html', require('ejs').renderFile);
app.set('json spaces', 2); //pretty print results for easier viewing later
...

Next, modify server/boot/root.js as follows:

module.exports = function(server) {
  var router = server.loopback.Router();
  router.get('/', function(req, res) {
    res.render('index');
  });
  server.use(router);
};

Finally, create the views directory by running:

$ mkdir -p server/views

Inside the views directory, create index.html with the following contents:

 Expand source
<DOCTYPE html>
<html>
  <head>
    <title>loopback-example-relation</title>
  </head>
  <body>
    <h1>loopback-example-relation</h1>
    <p>
      <a href="/explorer">API Explorer</a>
    </p>
    <h2>API</h2>
    <ul>
      <li><a href='/api/customers'>/api/customers</a>
      <li><a href='/api/customers?filter[fields][name]=true'>/api/customers?filter[fields][name]=true</a>
      <li><a href='/api/customers/1'>/api/customers/1</a>
      <li><a href='/api/customers/youngFolks'>/api/customers/youngFolks</a>
      <li><a href='/api/customers/1/reviews'>/api/customers/1/reviews</a>
      <li><a href='/api/customers/1/orders'>/api/customers/1/orders</a>
      <li><a href='/api/customers?filter[include]=reviews'>/api/customers?filter[include]=reviews</a>
      <li><a href='/api/customers?filter[include][reviews]=author'>/api/customers?filter[include][reviews]=author</a>
      <li><a href='/api/customers?filter[include][reviews]=author&filter[where][age]=21'>/api/customers?filter[include][reviews]=author&filter[where][age]=21</a>
      <li><a href='/api/customers?filter[include][reviews]=author&filter[limit]=2'>/api/customers?filter[include][reviews]=author&filter[limit]=2</a>
      <li><a href='/api/customers?filter[include]=reviews&filter[include]=orders'>/api/customers?filter[include]=reviews&filter[include]=orders</a>
    </ul>
  </body>
</html>

View what you have so far by entering this command:

$ cd loopback-example-relation
$ slc run server

Browse to localhost:3000 to see the home page.  Then click on API Explorer and you'll see the models you created.

Icon

You may notice some of the API endpoints return empty arrays or errors, because the database is empty. In addition, you need to define model relations for some of the API endpoints to work. Don't fret, you'll get to that very soon!

Add sample data

In server/boot, create a script named create-customers.js with the following contents:

var customers = [
  {name: 'Customer A', age: 21},
  {name: 'Customer B', age: 22},
  {name: 'Customer C', age: 23},
  {name: 'Customer D', age: 24},
  {name: 'Customer E', age: 25}
];

module.exports = function(server) {
  var dataSource = server.dataSources.db;
  dataSource.automigrate('Customer', function(er) {
    if (er) throw er;
    var Model = server.models.Customer;
    //create sample data
    var count = customers.length;
    customers.forEach(function(customer) {
      Model.create(customer, function(er, result) {
        if (er) return;
        console.log('Record created:', result);
        count--;
        if (count === 0) {
          console.log('done');
          dataSource.disconnect();
        }
      });
    });
    //define a custom scope
    Model.scope('youngFolks', {where: {age: {lte: 22 }}});
  });
};

Create two more scripts, create-reviews.js and create-orders.js in server/boot. This sample data will be automatically loaded when you start the application.

Icon

automigrate() recreates the database table/index if it already exists. In other words, existing tables will be dropped and ALL EXISTING DATA WILL BE LOST. For more information, see Creating a database schema from models. Note also that Model.scope() is only in create-customers.js.

Create model relations

You're going to create four relations between the models you just created.  The relations will describe that:

  • A customer has many reviews (Customer hasMany Review).
  • A customer has many orders (Customer hasMany Order).
  • A review belongs to a customer (Review belongsTo Customer).
  • An order belongs to a customer (Order belongsTo Customer).

From the project root, enter the command:

$ slc loopback:relation

Follow the prompts and create the following relationships:

Customer - hasMany Review

  • property name for the relation: reviews
  • custom foreign key: authorId

Customer - hasMany Order

  • property name for the relation: orders
  • custom foreign key: customerId

Review - belongsTo Customer

    • property name for the relation: author
    • custom foreign key: authorId

Order - belongsTo Customer

Icon

For any item without property name for the relation or custom foreign key, just use the defaults. LoopBack will derive these values automatically when you don't specify one.

When you're done, your common/models/customer.json should look like:

 Expand source
{
  "name": "Customer",
  "base": "PersistedModel",
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "number"
    }
  },
  "validations": [],
  "relations": {
    "reviews": {
      "type": "hasMany",
      "model": "Review",
      "foreignKey": "authorId"
    },
    "orders": {
      "type": "hasMany",
      "model": "Order",
      "foreignKey": "customerId"
    }
  },
  "acls": [],
  "methods": []
}
common/models/reviews.json should look like:
 Expand source
{
  "name": "Review",
  "base": "PersistedModel",
  "properties": {
    "product": {
      "type": "string"
    },
    "star": {
      "type": "number"
    }
  },
  "validations": [],
  "relations": {
    "author": {
      "type": "belongsTo",
      "model": "Customer",
      "foreignKey": "authorId"
    }
  },
  "acls": [],
  "methods": []
}

and common/models/order.json should look like:

 Expand source
{
  "name": "Order",
  "base": "PersistedModel",
  "properties": {
    "description": {
      "type": "string"
    },
    "total": {
      "type": "number"
    }
  },
  "validations": [],
  "relations": {
    "customer": {
      "type": "belongsTo",
      "model": "Customer",
      "foreignKey": ""
    }
  },
  "acls": [],
  "methods": []
}

Try the API

Restart application with slc run server and browse to localhost:3000.  Try out the API with the models and relations you defined.  The following table describes many of the API endpoints.

API EndpointDescription
/api/customersList all customers
/api/customers?filter[fields][0]=name

List all customers, but only return the name property for each result

/api/customers/1Look up a customer by ID

 /api/customers/youngFolks

 List a predefined scope named youngFolks

/api/customers/1/reviewsList all reviews posted by a given customer
/api/customers/1/ordersList all orders placed by a given customer
/api/customers?filter[include]=reviewsList all customers including their reviews
/api/customers?filter[include][reviews]=authorList all customers including their reviews which also include the author
/api/customers?filter[include][reviews]=author&filter[where][age]=21List all customers whose age is 21, including their reviews which also include the author
/api/customers?filter[include][reviews]=author&filter[limit]=2List first two customers including their reviews which also include the author
/api/customers?filter[include]=reviews&filter[include]=ordersList all customers including their reviews and orders