在最近学习Express.js技术时发现了IBM的一个优秀的基于Node.js的后端框架LoopBack。遗憾的是,国内对于它的介绍并不多(也的确有一个很浅的尝试分析)。


目前,就基于Node.js开发技术生态圈内,我还没有发现把丰富的前端与后端统一的框架方案。当然,这个问题本身提得也特别刁钻。撇开这个,我们还是看一下被IBM收购后的后端框架LoopBack技术吧。

原文如下:

----------------------------

IBM announced the acquisition of StrongLoop, creators of the LoopBack framework and active members of the Node.js community. This is a huge step for IBM and Node.js applications in the enterprise.

Node.js is an incredible tool for rapidly building highly performant and scalable back end systems, and you develop it using a familiar core language that most front-end developers are already accustomed to, JavaScript. Node.js is making serious inroads into the enterprise, with many major enterprises building their entire web presence and APIs services tier on top of a Node.js infrastructure.

StrongLoop’s LoopBack framework enables you to easily connect and expose your data as REST services. It provides the ability to visually create data models in a graphical (or command line) interface, which are used to automatically generate REST APIs – thus generating CRUD operations for your REST services tier, without having to write any code.

Why is this important? It makes API development easier and drastically reduces time from concept to implementation. Take a look at the video below to get an idea just what it can do for you.



IBM Cloudant is IBM’s NoSQL Database as a Service that offers massive scalability, powerful search features, high availability, and helps you deliver solutions faster, with lower maintenance.

While the acquisition of StrongLoop is still “hot off of the presses”, you can already use LoopBack and Cloudant together today to accelerate development of a highly secure REST application layer. Combine this with the IBM MobileFirst platform, and you have an even more comprehensive and secure mobile app solution. In fact, both StrongLoop/LoopBack and Cloudant now come as part of the MobileFirst Platform, but you can still get them as independent services on IBM Bluemix.

In this post we will examine how to leverage IBM Cloudant as a backend data store with the LoopBack framework and Strongloop Arc tools.

In the LoopBack framework, data is retrieved and persisted through what is referred to as “data connectors“. StrongLoop maintains several data connectors, with connectors for additional backend data stores provided by the developer community.

To persist our data in the Cloudant DBaaS service, we will be using the official LoopBack Cloudant Connector.

Setting Up IBM Cloudant

Setting up Cloudant is simple. A restricted on-premis license for Cloudant comes with the MobileFirst Platform Foundation Server, or you can add the Cloudant service to any app on Bluemix: you can use Cloudant standalone through the REST interface, or use the Node.js/Cloudant web starter template to have a Node.js plus Cloudant multi-tier solution.

Let’s examine how to setup Cloudant using the Bluemix starter template. Navigate your browser to the Node.js/Cloudant web starter template, and then enter an app name and app host (this will be used in the app’s URL prefix), and hit the “Create” button.

Node.js Cloudant Starter

Hitting the “Create” button creates a Cloudant NoSQL database instance, and a Node.js service instance on Bluemix where we can later deploy our application. A message will be displayed once your app is up and running. 

Once your services are up and running on Bluemix, you will be able to deploy your Node.js app directly using the Cloud Foundry Command Line Interface, use IBM Devops Services or Bluemix Live Sync & GIT for deployment, or use the Eclipse Tools for Bluemix.

Next we need to create our Cloudant Database instance for use with our Node.js/LoopBack app. From your Bluemix Dashboard, click on your “Cloudant NoSQL DB” service link to view details, and then click on the “Launch” button in the top right corner to view the Cloudant Dashboard.

Once you are in the Cloudant Dashboard, you will need to create a database instance to use with the app. Select the “Add New Database” link in the top right. You will be prompted to enter a database name, and then hit “Create”.

Create a new Cloudant database

Once the database has been created, you’re now ready to setup your local development environment and start building your app.

Local Development Environment

If you don’t already have them installed, you will need to download and install the following tools:

  1. Node.js – Head over to nodejs.org and follow the instructions to install Node.js.

  2. LoopBack – Next, head over to loopback.io and follow the instructions to download the LoopBack API Framework. You may also want to review the Getting Started Guide and documentation for additional information.

  3. Cloud Foundry CLI – The Cloud Foundry Command Line Interface is be used to manually deploy your app to Bluemix. Follow these instructions to install the CF CLIon your local development machine.

Setting up the Node.js/LoopBack App

Once you have your environment prerequisites installed, then you are now ready to create your API app.

The first thing that we need to do is setup the app using the LoopBack application generator from a command line terminal. You’ll need to specify the application name and subdirectory where the app will be created.

$ slc loopback

     _-----_
    |       |    .--------------------------.
    |--(o)--|    |  Let's create a LoopBack |
   `---------′   |       application!       |
    ( _′U`_ )    '--------------------------'
    /___A___
     |  ~  |
   __'.___.'__
 ′   `  |° ′ Y `

? What's the name of your application? business-directory
? Enter name of the directory to contain the project: business-directory

After creating the app, the next step is to create the connector that enables the LoopBack framework to store data in the Cloudant database. We will use the official Cloudant LoopBack Connector.

In your terminal window, cd into the project directory that was just generated, and then install the loopback-connector-cloudant using npm.

$ cd business-directory
$ npm install loopback-connector-cloudant

Once you’ve installed the connector, you need to configure Cloudant as a data source available to the LoopBack framework. This can be setup programmatically to extract credentials from your environment configuration. However, for simplicity, I will show the configuration with credentials in the datasource.json configuration file.

Open up the server/datasources.json file using a JavaScript/text editor, and add the “cloudant” entry shown below. You will need to specify your Cloudant host URL, plus authentication credentials providing access to the database.

{
    "db": {
        "name": "db",
        "connector": "memory"
    },
    "cloudant": {
        "name": "cloudant",
        "connector": "cloudant",
        "url": "{cloudant host url goes here}",
        "database": "business-directory"
    }
}

You can check the Cloudant Connector docs for additional configuration and usage information.

The loopback framework has two ways that you can build your data model: the command line interface, and the Arc Composer, which is a graphical interface for building your data model & API.

First, we will setup a data model using the command line tools. Using the slc loopback:model generator, walk through the following steps to create a “Company” entity in the data model.

$ slc loopback:model
? Enter the model name: Company
? Select the data-source to attach Company to: cloudant
? Select model's base class: PersistedModel
? Expose Company via the REST API? Yes
? Custom plural form (used to build REST URL): Companies
Let's add some Company properties now.

Enter an empty property name when done.
? Property name: name
? Property type: string
? Required? Yes

Let's add another Company property.
Enter an empty property name when done.
? Property name: address
? Property type: string
? Required? Yes

Let's add another Company property.
Enter an empty property name when done.
? Property name: city
? Property type: string
? Required? Yes

Let's add another Company property.
Enter an empty property name when done.
? Property name: state
? Property type: string
? Required? Yes

Let's add another Company property.
Enter an empty property name when done.
? Property name: zip
? Property type: string
? Required? Yes

This will generate the Company.json configuration for the data model, and Company.jsclass, which allows you to extend the class to add your own custom logic.

Company.json:

{
  "name": "Company",
  "plural": "Companies",
  "base": "PersistedModel",
  "idInjection": true,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "name": {
      "type": "string",
      "required": true
    },
    "address": {
      "type": "string",
      "required": true
    },
    "city": {
      "type": "string",
      "required": true
    },
    "state": {
      "type": "string",
      "required": true
    },
    "zip": {
      "type": "string",
      "required": true
    }
  },
  "validations": [],
  "relations": {},
  "acls": [],
  "methods": {}
}

Company.js:

module.exports = function(Company) {
  //add your custom extension logic here
};

Next, let’s create more classes in our data model using the Arc Composer interface. From your terminal window run the following command:

$ slc arc

This will start the StrongLoop Arc graphical interface for interacting with the LoopBack API/app, and open a browser window to the local service. Once the browser window opens, click on the “Composer” icon to enter the GUI for managing your data model.

In the Composer interface, click on the “Add New Model” link to start building the next class. Enter a model name “Employee”, and the plural form “Employees”, then be sure to select “PersistedModel” as the based model, and select “cloudant” as the data source.

Next, add the following three properties to the Employee model:

  • name, string, required

  • title, string, required

  • company, string, required

Once you save the model, two JavaScript files will be generated: Employee.json and Employee.js. Just like the Company model created from the command line (above), these represent the data model definition, and a class where you can extend the model to add your own custom logic or behaviors.

Employee.json:

{
  "name": "Employee",
  "base": "PersistedModel",
  "strict": false,
  "idInjection": false,
  "options": {
    "validateUpsert": true
  },
  "properties": {
    "name": {
      "type": "string",
      "required": true
    },
    "title": {
      "type": "string",
      "required": true
    },
    "companyId": {
      "type": "number",
      "required": true
    }
  },
  "validations": [],
  "relations": {},
  "acls": [],
  "methods": []
}

Employee.js:

module.exports = function(Employee) {
  //add your custom extension logic here
};

Now that we have created two data object definitions, let’s define a relationship between the two objects. On the command line, use the “slc loopback:relation” generator command to create a relationship.

slc loopback:relation
? Select the model to create the relationship from: Company
? Relation type: has many
? Choose a model to create a relationship with: Employee
? Enter the property name for the relation: employees
? Optionally enter a custom foreign key: company
? Require a through model? No

Now that we’ve defined data, and defined a relationship, let’s start interacting with the REST API that is automatically generated from our data model.

If you’re wondering how the relationships work b/c we didn’t define id values in our model, those id properties get automatically generated.

Interacting with the data/services

The LoopBack framework automatically generates REST endpoints for all of the objects within your data model. It also automatically generates Swagger API documentation. This gives you a simple and easy to use interface for all of the services generated by your project. Let’s explore the generated docs now…

Go back to the Arc Composer interface in the browser and click on the “App Controller” button (triangle “play” button in the top right), and start your app. Once your app is running, open up the API documentation in a new browser window: http://localhost:3000/explorer/(using the default local configuration).

From the generated documentation you can see all of the exposed REST methods for interacting with your data model. From here you can also interact and test these services directly. Just click on the “Get /Companies” entry to expand the service details. Use the “Try it out!” button to invoke this service. However, you’ll need to first use the “POST /Companies” method to insert some data before it can be queried.

You can go to the “POST /Companies” service to insert a new record with the following data:

{
  "name": "IBM",
  "address": "1 New Orchard Road",
  "city": "Armonk",
  "state": "NY",
  "zip": "10504"
}

This exact same service could have been invoked directly using a REST request.

Now, let’s query that data to ensure that it has been inserted into the database. From the command line terminal, run the following command to see the output of the /Companies query:

$ curl http://localhost:3000/api/Companies

You should see the query results similar to what I show below, except the id and _rev values will be different.

[{"name":"IBM","address":"1 New Orchard Road","city":"Armonk","state":"NY","zip":"10504","id":"a5bee38b6ce94b163de664fd5b7bc9f0","_rev":"1-646f0565f4abc14a3bc6876e05f23ef0"}]

Next, let’s add some employees using the “POST /Companies/{id}/employees” method. You could post data using the explorer, or directly to the REST service. In this case I inserted data directly to the API using curl, though I replaced the “{id}” with the actual database-generated id of the company.

$ curl -H "Content-Type: application/json"  -X POST -d '[{ "name": "Andrew Trice","title": "Developer Advocate"},{ "name": "Ray Camden","title": "Developer Advocate"},{ "name": "Marek Sadowski","title": "Developer Advocate"},{ "name": "Kevin Hoyt","title": "Developer Advocate"},{ "name": "Ken Parmelee","title": "Program Director"}]' http://localhost:3000/api/Companies/a5bee38b6ce94b163de664fd5b7bc9f0/employees

These services are available as RESTful endpoints, and can be consumed by any application, whether it is a mobile app, desktop app, or web based application.

Querying and Filtering Data

You can query all companies by invoking GET /Companies, all employees by invoking GET /Employees, or all employees for a particular company by calling /Companies/{id}/employees, but 99.99% of the time you won’t want to do that – it is too generic, simply returns too much data, and not necessarily in a logical or compartmentalized structure.

So, let’s use the LoopBack framework’s filters to narrow the search results. The LoopBack framework supports [where] filters to narrow the records that are returned, [limit] filters to limit the number of results returned, [fields] filters to limit the properties returned for an object, [order] to determine the results order, [include] filters to include related models, and [skip] filters to skip results (for data paging). All of these can be specified as parameters to the REST request.

Let’s take a look at some examples.

First, let’s return all Employees whose title is “Developer Advocate”:

$ curl -g 'http://localhost:3000/api/employees?filter[where][title]=Developer%20Advocate'

[{"name":"Andrew Trice","title":"Developer Advocate","company":"a5bee38b6ce94b163de664fd5b7bc9f0","id":"31c71d93e2ff40c1f789e4d46cd7f2a0","_rev":"1-582ef49e3963af0f0ab6f4d97f1038c6"},
{"name":"Marek Sadowski","title":"Developer Advocate","company":"a5bee38b6ce94b163de664fd5b7bc9f0","id":"63d7631797106ddfafcae31d2bc70ced","_rev":"1-0967f7cb1b26f9ee619e8c76e3e08e74"},
{"name":"Kevin Hoyt","title":"Developer Advocate","company":"a5bee38b6ce94b163de664fd5b7bc9f0","id":"732ad7d6c368579caea6aa7caea89e4b","_rev":"1-8e220575e11c3973a2b6a3bca02658d5"},
{"name":"Ray Camden","title":"Developer Advocate","company":"a5bee38b6ce94b163de664fd5b7bc9f0","id":"8436dee94003f63667657b2a78b2170c","_rev":"1-c0d797c65fec2e2e2cc3497a589ef2f2"}]

However, this is too much data for my current needs. Let’s only return the name field for this same set of results:

$ curl -g 'http://localhost:3000/api/employees?filter[fields][name]=true&filter[where][title]=Developer%20Advocate'

[{"name":"Andrew Trice"},
{"name":"Marek Sadowski"},
{"name":"Kevin Hoyt"},
{"name":"Ray Camden"}]

Now, consider that you might want to further limit the number of records that are returned:

$ curl -g 'http://localhost:3000/api/employees?filter[fields][name]=true&filter[where][title]=Developer%20Advocate&filter[limit]=3'

[{"name":"Andrew Trice"},
{"name":"Marek Sadowski"},
{"name":"Kevin Hoyt"}]

Now, what if you actually wanted those results in a different order. We can add filters to deliver the first 3 results after being reordered descending by name:

curl -g 'http://localhost:3000/api/employees?filter[fields][name]=true&filter[where][title]=Developer%20Advocate&filter[limit]=3&filter[order]=name%20DESC'

[{"name":"Ray Camden"},
{"name":"Marek Sadowski"},
{"name":"Kevin Hoyt"}]

Updating Data

You can easily update data by sending a PUT request to the API/id. So, if you want to update an employee, you can send a PUT request to /employees/{id} containing only the values that you want to update.

Let’s say I want to update my name on my employee entry… in this case we just send a PUT request to /Employees/{id} containing the new value for the “name” property:

$ curl -H "Content-Type: application/json" -X PUT -g -d '{"name":"Andy Trice"}' 'http://localhost:3000/api/Employees/31c71d93e2ff40c1f789e4d46cd7f2a0'

{"name":"Andy Trice","title":"Developer Advocate","company":"a5bee38b6ce94b163de664fd5b7bc9f0","id":"31c71d93e2ff40c1f789e4d46cd7f2a0","_rev":"6-88e83684b52cde686c08a84555e36d34"}

Moving to the Cloud

So far we’ve been testing everything on our local development machines. If you manage your own Node.js server, you can simply deploy your code to your production environment.

We also have several options to move this application to the cloud. First, we’ll examine the Node.js instant runtime, which is part of the Cloudant application template that we used earlier in this post. We also have the option to deploy this API to the StrongLoop Process Manager running in a container on Bluemix.

Node.js Instant Runtime

Since we started this post by creating an app on Bluemix, let’s go ahead and deploy it to that Bluemix instant runtime.

We’re going to use the Cloud Foundry Command Line API to deploy the Node.js application to Bluemix, but first we need to make some minor changes to our package.json file. Open package.json in a code editor and add the “dependencies” and “bundle dependencies” as shown in the sample package.json directly below:

{
  "name": "business-directory",
  "version": "1.0.0",
  "main": "server/server.js",
  "scripts": {
    "pretest": "jshint ."
  },
  "dependencies": {
    "compression": "^1.0.3",
    "cors": "^2.5.2",
    "errorhandler": "^1.1.1",
    "loopback": "^2.14.0",
    "loopback-boot": "^2.6.5",
    "loopback-datasource-juggler": "^2.19.0",
    "serve-favicon": "^2.0.1",
    "loopback-connector-cloudant": "*",
    "cfenv": "1.0.x"
  },
  "optionalDependencies": {
    "loopback-explorer": "^1.1.0"
  },
  "devDependencies": {
    "jshint": "^2.5.6"
  },
  "repository": {
    "type": "",
    "url": ""
  },
  "description": "business-directory",
  "bundleDependencies": [
    "compression",
    "cors",
    "errorhandler",
    "loopback",
    "loopback-boot",
    "loopback-connector-cloudant",
    "loopback-datasource-juggler",
    "loopback-explorer",
    "serve-favicon"
  ]
}

Next, we need to log in using the CF CLI in a terminal window:

$ cf login

Follow the prompts to log into Bluemix.

Next, use “cf push {app name}” to deploy your application to Bluemix Note the “-c” option, which tells node what command to start with. This needs to be specified because it is slightly different than the default Bluemix Node.js app configuration:

$ cf push LoopBack-Directory -c "node server/server.js"

Once your app has been deployed to Bluemix, you should see a summary similar to the following:

requested state: started
instances: 1/1
usage: 512M x 1 instances
urls: loopback-directory.mybluemix.net
last uploaded: Wed Aug 26 03:48:50 UTC 2015
stack: lucid64
buildpack: SDK for Node.js(TM) (ibm-node.js-0.12.7)

     state     since                    cpu    memory           disk           details
#0   running   2015-08-25 11:50:29 PM   0.1%   120.5M of 512M   119.8M of 1G

Now that is has been deployed to Bluemix, the API service is available for use within your applications. You can take a look at the API explorer and API endpoints for yourself at: loopback-directory.mybluemix.net/explorer

Note: I restricted write access, so you can only read the data.

StrongLoop Process Manager

Our second option to deploy our LoopBack app into production is to use a StrongLoop Process Manager Container on Bluemix. To get started, navigate to the Bluemix Catalog and select the “ibm-node-strong-pm” p_w_picpath.

When prompted, enter a container name, then be sure to request and bind a public IP, and select ports for the app to use. You will need port 8701 (used by the Process Manager), and the port that your app runs on, the default used by your app is 3001, but you can configure your app’s port in the server/config.json file.

Make sure that the requested port for the container matches the port used by the app in config.json, otherwise you will not be able to access the app once deployed. This can be a standard port like HTTP port 80, or a one-off port like the default 3001, which is generally used for development.

You will have the ability to deploy your app to the remote instance on Bluemix using the StrongLoop Process Manager tool on your local machine once your container has been created.

If you still have the StrongLoop Arc/Composer tool open in your browser, go to that now. If you don’t still have that running, go to your command line termainl and run the “slc arc” command from your app’s directory.

$ cd business-directory
$ slc arc

The Arc tool will be launched in your browser automatically. Next, wee need to set up a PM host for our remote container. Go to the “Process Manager” view and select the “Add PM Host” option to add the newly created container by IP address and port.

You can get the IP address and verify the bound ports by viewing your container p_w_picpath in your Bluemix Dashboard.

Next, go to the Build & Deploy module within the Arc tool. From here, select or build an archive that you’d like to deploy, then select the host where you want to deploy your application. Once you hit the “Deploy” button the app will be deployed.

Once your app has been deployed, it will be ready for you to use on the IP and port that were specified. You may need to jump back over to the Process Manager module to make sure your app instance is up and running if you aren’t able to hit it directly.

Next Steps

Once your app’s API is live and in production, you can consume it from any application that can consume REST services – be it mobile, desktop, or web based.

LoopBack and IBM MobileFirst

Above I mentioned that you can already integrate APIs generated by the LoopBack framework with apps leveraging IBM MobileFirst. Check out Getting Started With IBM MobileFirst and Node.js APIs built with LoopBack for additional detail how to configure your MobileFirst Platform Foundation server and LoopBack/Node.js app to have MobileFirst manage authentication and access to Node.js APIs, plus capture analytic information that will be reported in the MobileFirst Analytics console.

----------------------------