Adjust the lines to fit into width of 80

This commit is contained in:
Raymond Feng 2013-08-28 21:41:11 -07:00
parent 8674e67416
commit 28cf9bdd1e
2 changed files with 168 additions and 118 deletions

View File

@ -1,40 +1,52 @@
# LoopBack DataSource and Connector Guide
## Overview
LoopBack is centered around models, which represent data and behaviors. The concept of `DataSource` is introduced to
encapsulate business logic to exchange data between models and various data sources. Data sources are typically databases
that provide create, retrieve, update, and delete (CRUD) functions. LoopBack also generalize other backend services,
such as REST APIs, SOAP Web Services, and Storage Services, as data sources.
LoopBack is centered around models, which represent data and behaviors. The
concept of `DataSource` is introduced to encapsulate business logic to exchange
data between models and various data sources. Data sources are typically
databases that provide create, retrieve, update, and delete (CRUD) functions.
LoopBack also generalize other backend services, such as REST APIs, SOAP Web
Services, and Storage Services, as data sources.
Data sources are backed by connectors which implement the data exchange logic using database drivers or other client APIs.
In general, connectors are not used directly by application code. The `DataSource` class provides APIs to configure the
underlying connector and exposes functions via `DataSource` or model classes.
Data sources are backed by connectors which implement the data exchange logic
using database drivers or other client APIs. In general, connectors are not used
directly by application code. The `DataSource` class provides APIs to configure
the underlying connector and exposes functions via `DataSource` or model classes.
![model-datasource-connector](datasource-connector.png "LoopBack Model, DataSource, and Connector")
The diagram above illustrates the relationship between LoopBack `Model`, `DataSource`, and `Connector`.
The diagram above illustrates the relationship between LoopBack `Model`,
`DataSource`, and `Connector`.
1. Define the `Model` using [LoopBack Definition Language (LDL)](definition-language.md). Now we have a model definition in
plain JSON or JavaScript object.
1. Define the Model using [LoopBack Definition Language (LDL)](definition-language.md).
Now we have a model definition in plain JSON or JavaScript object.
2. Create an instance of `ModelBuilder` or `DataSource`. Please note that `DataSource` extends from `ModelBuilder`.
`ModelBuilder` is responsible for compiling model definitions to JavaScript constructors representing model classes.
`DataSource` inherits that function from `ModelBuilder`. In addition, `DataSource` adds behaviors to model classes by
mixing in methods from the `DataAccessObject` into the model class.
2. Create an instance of ModelBuilder or DataSource. Please note that
DataSource extends from ModelBuilder. ModelBuilder is responsible for compiling
model definitions to JavaScript constructors representing model classes.
DataSource inherits that function from ModelBuilder. In addition, DataSource
adds behaviors to model classes by mixing in methods from the DataAccessObject
into the model class.
3. Use `ModelBuilder` or `DataSource` to build a JavaScript constructor (i.e, the model class) from the model definition.
Model classes built from `ModelBuilder` can be later attached to a `DataSource` to receive the mixin of data access functions.
3. Use ModelBuilder or DataSource to build a JavaScript constructor (i.e,
the model class) from the model definition. Model classes built from ModelBuilder
can be later attached to a DataSource to receive the mixin of data access
functions.
4. As part of step 2, `DataSource` initializes the underlying `Connector` with a `settings` object which provides configurations
to the connector instance. `Connector` collaborates with `DataSource` to define the functions as `DataAccessObject` to be mixed
into the model class. The `DataAccessObject` consists of a list of static and prototype methods. It can be CRUD operations
4. As part of step 2, DataSource initializes the underlying Connector with
a settings object which provides configurations to the connector instance.
Connector collaborates with DataSource to define the functions as
DataAccessObject to be mixed into the model class. The DataAccessObject
consists of a list of static and prototype methods. It can be CRUD operations
or other specific functions depending on the connector's capabilities.
## LoopBack DataSource
DataSource is the unified interface for LoopBack applications to integrate with backend systems. It's a factory for
data access logic around model classes. With the ability to plug in various connectors, DataSource provides the necessary
abstraction to interact with databases or services to decouple the business logic from plumbing technologies.
DataSource is the unified interface for LoopBack applications to integrate with
backend systems. It's a factory for data access logic around model classes. With
the ability to plug in various connectors, DataSource provides the necessary
abstraction to interact with databases or services to decouple the business
logic from plumbing technologies.
### Creating a DataSource
@ -59,14 +71,17 @@ The `connector` argument passed the DataSource constructor can be one of the fol
* The connector module from `require(connectorName)`
* The full name of the connector module, such as 'loopback-connector-oracle'
* The short name of the connector module, such as 'oracle', which will be converted to 'loopback-connector-<shortName>'
* The short name of the connector module, such as 'oracle', which will be converted
to 'loopback-connector-<shortName>'
* A local module under ./connectors/<connectorName> folder
var ds1 = new DataSource('memory');
var ds2 = new DataSource('loopback-connector-mongodb'));
var ds3 = new DataSource(require('loopback-connector-oracle'));
**Note**: LoopBack provides a built-in connector named as `memory` to use in-memory store for CRUD operations.
**Note**: LoopBack provides a built-in connector named as `memory` to use in-memory
store for CRUD operations.
#### settings
@ -84,8 +99,9 @@ For connector-specific settings refer to connector's readme file.
## Creating a Model
`DataSource` extends from `ModelBuilder`, which is a factory for plain model classes that only have properties.
`DataSource` connected with specific databases or other backend systems using `Connector`.
`DataSource` extends from `ModelBuilder`, which is a factory for plain model
classes that only have properties. `DataSource` connected with specific databases
or other backend systems using `Connector`.
var DataSource = require('loopback-datasource-juggler').DataSource;
var ds = new DataSource('memory');
@ -98,11 +114,12 @@ For connector-specific settings refer to connector's readme file.
age: Number
});
All model classes within single data source shares same connector type and one database
connection or connection pool. But it's possible to use more than one data source to connect with
different databases.
All model classes within single data source shares same connector type and one
database connection or connection pool. But it's possible to use more than one
data source to connect with different databases.
Alternatively, a plain model constructor created from `ModelBuilder` can be attached a `DataSource`.
Alternatively, a plain model constructor created from `ModelBuilder` can be
attached a `DataSource`.
var ModelBuilder = require('loopback-datasource-juggler').ModelBuilder;
var builder = new ModelBuilder();
@ -123,15 +140,16 @@ Alternatively, a plain model constructor created from `ModelBuilder` can be atta
## More DataSource Features
In addition to data access functions mixed into the model class, `DataSource` also provides APIs to interact with the
underlying backend system.
In addition to data access functions mixed into the model class, `DataSource`
also provides APIs to interact with the underlying backend system.
### Discovering model definitions from the data source
Some connectors provide discovery capability so that we can use DataSource to discover model definitions from existing
database schema.
Some connectors provide discovery capability so that we can use DataSource to
discover model definitions from existing database schema.
The following APIs allow UI or code to discover database schema definitions that can be used to build LoopBack models.
The following APIs allow UI or code to discover database schema definitions that
can be used to build LoopBack models.
// List database tables and/or views
ds.discoverModelDefinitions({views: true, limit: 20}, cb);
@ -174,20 +192,22 @@ You can also discover and build model classes in one shot:
});
});
In addition to the asynchronous APIs, `DataSource` also provides the synchronous ones. Please refer to the DataSource
API references.
In addition to the asynchronous APIs, `DataSource` also provides the synchronous
ones. Please refer to the DataSource API references.
### Synchronizing model definitions against the data source
DataSource instance have two methods for updating db structure: `automigrate` and `autoupdate` for relational
databases.
DataSource instance have two methods for updating db structure: `automigrate` and
`autoupdate` for relational databases.
The `automigrate` method drop table (if exists) and create it again, `autoupdate` method generates
ALTER TABLE query. Both method accepts an optional array of model names and a callback function to be
called when migration/update done. If the `models` argument is not present, all models are checked.
The `automigrate` method drop table (if exists) and create it again, `autoupdate`
method generates ALTER TABLE query. Both method accepts an optional array of
model names and a callback function to be called when migration/update done. If
the `models` argument is not present, all models are checked.
In the following example, we create first version of the CustomerTest model, use `automigrate` to create the database
table, redefine the model with second version, and use `autoupdate` to alter the database table.
In the following example, we create first version of the CustomerTest model, use
`automigrate` to create the database table, redefine the model with second
version, and use `autoupdate` to alter the database table.
// Create the 1st version of 'CustomerTest'
ds.createModel(schema_v1.name, schema_v1.properties, schema_v1.options);
@ -229,7 +249,8 @@ and a `callback` argument, which receive boolean value depending on db state:
## LoopBack Connector
Connectors implement the logic to integrate with specific backend systems, such as databases or REST services.
Connectors implement the logic to integrate with specific backend systems, such
as databases or REST services.
### LoopBack Connector Modules
@ -246,7 +267,8 @@ Connectors implement the logic to integrate with specific backend systems, such
#### Initializing connector
The connector module can export an `initialize` function to be called by the owning DataSource instance.
The connector module can export an `initialize` function to be called by the
owning DataSource instance.
exports.initialize = function (dataSource, postInit) {
@ -258,26 +280,29 @@ The connector module can export an `initialize` function to be called by the own
...
};
The DataSource calls the `initialize` method with itself and an optional postInit callback function. The connector
receives the settings from the dataSource argument and use it to configure connections to backend systems.
The DataSource calls the `initialize` method with itself and an optional `postInit`
callback function. The connector receives the settings from the `dataSource`
argument and use it to configure connections to backend systems.
Please note connector and dataSource set up a reference to each other.
Upon initialization, the connector might connect to database automatically. Once connection established dataSource
object emit 'connected' event, and set `connected` flag to true, but it is not necessary to wait for 'connected' event
Upon initialization, the connector might connect to database automatically.
Once connection established dataSource object emit 'connected' event, and set
`connected` flag to true, but it is not necessary to wait for 'connected' event
because all queries cached and executed when dataSource emit 'connected' event.
To disconnect from database server call `dataSource.disconnect` method. This call forwarded to connector if connector
have ability to connect/disconnect.
To disconnect from database server call `dataSource.disconnect` method. This
call is forwarded to the connector if the connector have ability to connect/disconnect.
#### Accessing data/services
The connector instance can have an optional property named as DataAccessObject that provides static and prototype methods to
be mixed into the model constructor. DataSource has a built-in DataAccessObject to support CRUD operations. The connector
can choose to use the CRUD DataAccessObject or define its own.
The connector instance can have an optional property named as DataAccessObject
that provides static and prototype methods to be mixed into the model constructor.
DataSource has a built-in DataAccessObject to support CRUD operations. The
connector can choose to use the CRUD DataAccessObject or define its own.
When a method is invoked from the model class or instance, it's delegated to the DataAccessObject which is backed by
the connector.
When a method is invoked from the model class or instance, it's delegated to the
DataAccessObject which is backed by the connector.
For example,
@ -285,9 +310,9 @@ For example,
## Building your own connectors
LoopBack connectors provide access to backend systems including databases, REST APIs
and other services. Connectors are not used directly by application code. We create
a DataSource to interact with the connector.
LoopBack connectors provide access to backend systems including databases, REST
APIs and other services. Connectors are not used directly by application code.
We create a DataSource to interact with the connector.
For example,
@ -305,7 +330,8 @@ For example,
### Implementing a generic connector
A connector module can implement the following methods to interact with the data source.
A connector module can implement the following methods to interact with the data
source.
exports.initialize = function (dataSource, postInit) {
@ -345,8 +371,8 @@ Another way is to directly export the connection function which takes a settings
### Implementing a CRUD connector
To support CRUD operations for a model class that is attached to the dataSource/connector, the connector needs to provide
the following functions:
To support CRUD operations for a model class that is attached to the
dataSource/connector, the connector needs to provide the following functions:
/**
* Create a new model instance

View File

@ -1,8 +1,9 @@
# LoopBack Definition Language Guide
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.
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 simple model
@ -20,9 +21,10 @@ The model simply defines a `user` model that consists of three properties:
* 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.
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:
@ -32,7 +34,8 @@ LDL supports a list of built-in types, including the basic types from JSON:
* Array
* Object
**Note**: The type name is case-insensitive, i.e., either "Number" or "number" can be used.
**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:
@ -42,16 +45,18 @@ The same model can also be described in JavaScript code:
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.
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.
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.
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:
@ -86,11 +91,13 @@ Here is an example,
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.
At this point, the constructor only has a set of accessors to model properties.
No behaviors have been introduced yet.
## Adding logic to a model
Models describe the shape of data. To leverage the data, we'll add logic to the model for various purposes, such as:
Models describe the shape of data. To leverage the data, we'll add logic to the
model for various purposes, such as:
- Interact with the data store for CRUD
- Add behavior around a model instance
@ -131,8 +138,9 @@ A plain model constructor created from `ModelBuilder` can be attached a `DataSou
User.attachTo(ds); // The CRUD methods will be mixed into the User constructor
### Manually add methods to the model constructor
Static methods can be added by declaring a function as a member of the model constructor.
Within a class method, other class methods can be called using the model as usual.
Static methods can be added by declaring a function as a member of the model
constructor. Within a class method, other class methods can be called using the
model as usual.
// Define a static method
@ -144,8 +152,8 @@ Within a class method, other class methods can be called using the model as usua
console.log(users); // Print an array of user instances
});
Instance methods can be added to the prototype. Within instance methods, the model instance itself can be referenced
with this keyword.
Instance methods can be added to the prototype. Within instance methods, the
model instance itself can be referenced with this keyword.
// Define a prototype method
User.prototype.getFullName = function () {
@ -158,7 +166,8 @@ with this keyword.
## Exploring advanced LDL features
As we mentioned before, a complete model definition is an object with three properties:
As we mentioned before, a complete model definition is an object with three
properties:
- name: The model name
- options: An object of options, optional
@ -176,8 +185,9 @@ There are a set of options to control the model definition.
- false: No `id` property will be added to the model
- Data source specific mappings
The model can be decorated with connector-specific options to customize the mapping between the model and the connector.
For example, we can define the corresponding schema/table names for Oracle as follows:
The model can be decorated with connector-specific options to customize the
mapping between the model and the connector. For example, we can define the
corresponding schema/table names for Oracle as follows:
{
"name": "Location",
@ -192,9 +202,11 @@ For example, we can define the corresponding schema/table names for Oracle as fo
}
### Property definitions
A model consists of a list of properties. The basic example use `propertyName: type` to describe a property.
A model consists of a list of properties. 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:
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"}
@ -222,7 +234,8 @@ LDL supports array types as follows:
- `{emails: [{type: String, length: 64}]}`
##### Object types
A model often has properties that consist of other properties. For example, the user model can have an `address` property
A model often has properties that consist of other properties. For example, the
user model can have an `address` property
that in turn has properties such as `street`, `city`, `state`, and `zipCode`.
LDL allows inline declaration of such properties, for example,
@ -239,9 +252,11 @@ LDL allows inline declaration of such properties, for example,
...
}
The value of the address is the definition of the `address` type, which can be also considered as an anonymous model.
The value of the address is the definition of the `address` type, which can be
also considered as an anonymous model.
If you intend to reuse the address model, we can define it independently and reference it in the user model. For example,
If you intend to reuse the address model, we can define it independently and
reference it in the user model. For example,
var AddressModel = {
street: String,
@ -261,26 +276,29 @@ If you intend to reuse the address model, we can define it independently and ref
var User = ds.define('User', UserModel);
**Note**: The user model has to reference the Address constructor or the model name - `'Address'`.
**Note**: The user model has to reference the Address constructor or the model
name - `'Address'`.
#### ID(s) for a model
A model representing data to be persisted in a database usually has one or more properties as an id to uniquely
identify the model instance. For example, the `user` model can have user ids.
A model representing data to be persisted in a database usually has one or more
properties as an id to uniquely identify the model instance. For example, the
`user` model can have user ids.
By default, if no id properties are defined and the `idInjection` of the model options is false, LDL will automatically
add an id property to the model as follows:
By default, if no id properties are defined and the `idInjection` of the model
options is false, LDL will automatically add an id property to the model as follows:
id: {type: Number, generated: true, id: true}
To explicitly specify a property as `id`, LDL provides an `id` property for the option. The value can be true, false,
or a number.
To explicitly specify a property as `id`, LDL provides an `id` property for the
option. The value can be true, false, or a number.
- true: It's an id
- false or any falsey values: It's not an id (default)
- a positive number, such as 1 or 2: It's the index of the composite id
LDL supports the definition of a composite id that has more than one properties. For example,
LDL supports the definition of a composite id that has more than one properties.
For example,
var InventoryDefinition =
{
@ -313,8 +331,9 @@ Format conversions can also be declared as options, for example:
* format: Format a Date
#### Mapping
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:
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}
@ -323,10 +342,11 @@ Oracle database table, you can use the following syntax:
#### hasMany
A `hasMany` relation builds a one-to-many connection with another model. You'll often find this relation on
the "other side" of a `belongsTo` relation. This relation indicates that each instance of the model has zero
or more instances of another model. For example, in an application containing users and posts, a user has zero
or more posts. For example,
A `hasMany` relation builds a one-to-many connection with another model. You'll
often find this relation on the "other side" of a `belongsTo` relation. This
relation indicates that each instance of the model has zero or more instances
of another model. For example, in an application containing users and posts, a
user has zero or more posts. For example,
// setup relationships
User.hasMany(Post, {as: 'posts', foreignKey: 'userId'});
@ -352,8 +372,8 @@ Example:
Book.hasMany('chapters', {model: Chapter, foreignKey: 'chapter_id'});
Scope methods created on the base model by hasMany allows to build, create and query instances of other class.
For example:
Scope methods created on the base model by hasMany allows to build, create and
query instances of other class. For example,
Book.create(function(err, book) {
// using 'chapters' scope for build:
@ -374,14 +394,16 @@ For example:
#### belongsTo
A `belongsTo` relation sets up a one-to-one connection with another model, such that each instance of the declaring
model "belongs to" one instance of the other model. For example, if your application includes users and posts,
and each post can be written by exactly one user.
A `belongsTo` relation sets up a one-to-one connection with another model, such
that each instance of the declaring model "belongs to" one instance of the other
model. For example, if your application includes users and posts, and each post
can be written by exactly one user.
Post.belongsTo(User, {as: 'author', foreignKey: 'userId'});
The code above basically says Post has a reference called `author` to User using the `userId` property of Post as the
foreign key. Now we can access the author in one of the following styles:
The code above basically says Post has a reference called `author` to User using
the `userId` property of Post as the foreign key. Now we can access the author
in one of the following styles:
post.author(callback); // Get the User object for the post author asynchronously
@ -389,9 +411,10 @@ foreign key. Now we can access the author in one of the following styles:
post.author(user) // Set the author to be the given user
#### hasAndBelongsToMany
A `hasAndBelongsToMany` relation creates a direct many-to-many connection with another model, with no
intervening model. For example, if your application includes users and groups, with each group having many users
and each user appearing in many groups, you could declare the models this way,
A `hasAndBelongsToMany` relation creates a direct many-to-many connection with
another model, with no intervening model. For example, if your application
includes users and groups, with each group having many users and each user
appearing in many groups, you could declare the models this way,
User.hasAndBelongsToMany('groups', {model: Group, foreignKey: 'groupId'});
user.groups(callback); // get groups of the user
@ -401,8 +424,9 @@ and each user appearing in many groups, you could declare the models this way,
### Extend from a base model
LDL allows a new model to extend from an existing model. For example, Customer can extend from User as follows. The Customer
model will inherit properties and methods from the User model.
LDL allows a new model to extend from an existing model. For example, Customer
can extend from User as follows. The Customer model will inherit properties and
methods from the User model.
var Customer = User.extend('customer', {
accountId: String,
@ -410,8 +434,8 @@ model will inherit properties and methods from the User model.
});
### Mix in model definitions
Some models share the common set of properties and logic around. LDL allows a model to mix in one or more other models.
For example,
Some models share the common set of properties and logic around. LDL allows a
model to mix in one or more other models. For example,
var TimeStamp = modelBuilder.define('TimeStamp', {created: Date, modified: Date});
var Group = modelBuilder.define('Group', {groups: [String]});