Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

NameRequiredDescription
fieldstrueAn object that represents the model’s schema defined as key-value pairs. The key is the name of the field and the value is the fields object. See the next table for details.
connectortrueConnector to which the model is bound (string). Each model can only have one connector. Connectors are responsible for reading and writing data from/to their data source.
documentedfalseSince Release 5.0.0. Determines whether to generate API documentation (true) or not (false). The default value is true.
metadatafalseUsed to provide connector specific configuration (for example, mapping the model to a specific database table for the MySQL connector or defining the join properties).
autogenfalseUsed to determine whether to generate API endpoints directly from the model. The default value is true. If the endpoint is auto-generated, you do not need to create an API endpoint definition.
actionsfalseAn array of data operations supported by the model. The valid values are: create, read, update, and delete. By default, all are supported by the model.
pluralfalse

A string used as the property name when your API endpoint returns an array. By default, the plural value is the plural of the model name. For example, if your model is named car, the default plural would be cars.

Note: this

Note

This value can be set on an API or a model.

singularfalse

A string used as the property name when your API endpoint returns a single record. By default, the singular value is the name of the model.

Note: this

Note

This value can be set on an API or a model.

beforefalseOne or more blocks to be executed before the request. Blocks are referenced by their name property. If you want to execute multiple blocks, you should specify them as an array of block names. If multiple blocks are specified, they are executed in the order specified.
afterfalseOne or more blocks to be executed after the request. Blocks are referenced by their name property. If you want to execute multiple blocks, you should specify them as an array of block names. If multiple blocks are specified, they are executed in the order specified.

...

The Model below extends the employee model employee model by adding the headquarters field to it.

Code Block
titlemodels/fullemp.js
languagejs
var Arrow = require('arrow');

var fullEmp = Arrow.Model.extend('employee','fullEmp',{
    fields: {
        headquarters:{type:Boolean, custom:true, 
            get:function(val,key,model){
                return model.get('state') === 'CA';
            }
        }
    }
});

module.exports = fullEmp;

Create a composite model

Composite models allow you to create a single model that is composed of one or more models based on the same or different connectors. Composite models can be joined together via a common set of properties, such as primary keys or foreign keys, or they can have no properties in common at all. The power of composite models is that you can represent multiple data sources and entities as a single API endpoint, which is ideal for many mobile use cases.

To create a composite model, follow the same procedure when creating a regular model except the connector property must be set to appc.composite, each field in the definition object must specify the model property to indicate which model the field originates from, and the metadata property must define the join operation to combine the models or leave it undefined to perform no join operations.

The following terms are used to refer to models:

  • Model definition: The composite model which is being created
  • Main model: The main source of data for the composite model. This is the left table in SQL terminology. It is implicitly defined.
  • Secondary model: Any model other than the main model. This will be the right table in SQL terminology.

The composite connector can either perform a left join or inner join:

  • left join: all records from the main model are returned regardless if it found a match in the secondary models
  • inner join: only records that match both models are returned

...

Join object definition

KeyTypeValue
modelStringName of the model. For left joins, this is the secondary model you want to join with the main model.
join_propertiesObjectCollection of key-value pairs that determine the keys in each model to perform the join operation. The key is the property of the model defined in this object and the value is the property to join in another model (or the main model for left joins).
multipleBooleanDetermines whether the match is one-to-one (false) or one-to-many (true). The default value is false. If true, the field being joined on must be of type Arrayand have a name property referring to the field from the secondary model to be used.

Left join example

The example below combines the employee and managers models to create the employee_manager modelmanager model. The models are joined based on a match between the managers model's employee_id and the employee model's auto-generated id.

...

Code Block
languagejs
collapsetrue
var Arrow = require('arrow');

// create a model from a mysql table
var employee_composite = Arrow.createModel('employee_composite',{
    fields: {
        fname: {type: String, description: 'First name', name: 'fname', model: 'employee'},
        manager: {type: String, description: 'Manager of employee', name: 'manager', model: 'employee_manager'},
        habits: {type: Array, description: 'Habits of employee', name: 'description', model: 'employee_habit'}
    },
    connector: 'appc.composite',
    metadata: {
        inner_join: [
            {
                model: 'employee_manager',
                join_properties: {
                    employee_id: 'id'
                }
            },
            {
                model:'employee_habit',
                multiple: true,
                join_properties:{
                    employee_id:'id'
                }
            }
        ]
    }
});

module.exports = employee_composite;

Field name mappings

You often want the ability to use a field property name in your model that is different from its name in an existing model. The following example shows how you can use the name sub-property of a field to map a model property name to a specific property name of an existing custom model or connector generated model. For example, the employee model has a property called first_name, but the new model wants that property to be called fname. The API Builder framework ensures this mapping occurs bidirectionally. 

Code Block
languagejs
var Arrow = require('arrow');

var emp = Arrow.Model.reduce('appc.mysql/employee','emp',{
    fields: {
        fname: { type:String, description:'First name', name:'first_name', required:true},
        lname: { type:String, description:'Last name', required:true, name:'last_name'},
        email: { type:String, description:'Email address', readonly:true, name:'email_address'}
    },
    connector: 'appc.mysql'
});

module.exports = emp;

Field input validation

You might need to perform validation on a field when creating or updating a record. Each property in your model definition can specify a validation function using the validator field property. This function is called before sending data to your model’s connector. The validator function is passed the value of the property. If the value is valid, the function should return null or undefined. If not valid, the function should return a message indicating why the validation failed. The following is an example of a validator function on a field.

Code Block
languagejs
var Arrow = require('arrow');

var emp = Arrow.Model.reduce('appc.mysql/employee','emp',{
    fields: {
        fname: {
            type:String, description:'First name', name:'first_name', required:true,
            validator:function(val) {
                if (val.length < 5) {
                    return 'First name must be greater than 5 characters'
                }
            }
        },
        lname: { type:String, description:'Last name', required:true, name:'last_name'},
        email: { type:String, description:'Email address', readonly:true, name:'email_address'}
    },
    connector: 'appc.mysql'
});

module.exports = emp;

Model input validation

You might need to perform validation on a whole model. Specify in your model definition a validation function using the validator model property. This function is called before sending data to your model’s connector. The validator function is passed the instance of the model. If the value is valid, the function should return null or undefined. If not valid, the function should return a message indicating why the validation failed or throw an exception. The following is an example of a validator function on a model.

Code Block
languagejs
var Arrow = require('arrow');

var emp = Arrow.Model.reduce('appc.mysql/employee','emp',{
    fields: {
        fame: { type:String },
        lname: { type:String }
    },
    validator: function (instance) {
        var errors = [];
        if (instance.get('fame') === "Rick") {
            errors.push('Sorry, Rick is not allowed to play here.');
        }
        if (instance.get('lname').length < 5) {
            errors.push('The lname must be at least 5 characters long.');
        }
        if (errors.length) {
            return errors.join('\n');
        }
    },
    connector: 'appc.mysql'
});

module.exports = emp;

Customizing generated model APIs

You can customize the generated APIs for your models. For example, by default, the create API only returns a status 201 with a header Location pointing to the newly created instance. No content is returned in the body. If you want to directly receive the newly created instance in the body of the request, add the includeResponseBody: true metadata to your model.

Code Block
languagejs
var Arrow = require('arrow');

var emp = Arrow.Model.reduce('appc.mysql/employee','emp',{
    fields: {
        fname: {
            type:String, description:'First name', name:'first_name', required:true
        },
        lname: { type:String, description:'Last name', required:true, name:'last_name'},
        email: { type:String, description:'Email address', readonly:true, name:'email_address'}
    },
    connector: 'appc.mysql',
    metadata: {
        includeResponseBody: true
    }
});

module.exports = emp;

Programmatic CRUD interface

All models inherit the CRUD interfaces supported by their underlying connector. As a result, you can programmatically call these interfaces. The main use case for using a model’s CRUD interface is when you want more control of an API's functionality. You can place logic in your API endpoint’s action function to handle custom business functionality and control execution of data access.

The following are the main interfaces most connectors support.

Code Block
languagejs
 // delete all records for a model
Model.deleteAll(callback);

// query a model.
Model.query(options, callback);

// find all records for a model
Model.findAll(callback);

// find a record by id for a model
Model.findByID(id, callback);

// delete a record for a model
Model.delete(instance, callback);

// update a record
Model.update(instance, callback);

// create a record
Model.create(object, callback);

The following model has example uses.

Code Block
languagejs
// example model
Model = Arrow.Model.extend(testTableName, {
    fields: {
        title: { type: String },
        content: { type: String }
    },
    connector: 'appc.mssql'
});

Delete all records

Use the deleteAll function on a model to delete all of its records.

Code Block
languagejs
Model.deleteAll(function(err) {
    if (err) {
        return next(err);
    }
    next();
});

Create, update, delete a record

The following is an example of creating a record and then updating and deleting it. It’s not necessarily a practical example but demonstrates how to use some additional interfaces available on a model.

Code Block
languagejs
collapsetrue
// setup record object
var title = 'Test',
    content = 'Hello world',
    object = {
        title: title,
        content: content
    };

// create record then update then delete
Model.create(object, function(err, instance) {
    if (err) {
        // do something
    }

    // update instance
    instance.set('content', 'foo');

    // save instance
    instance.update(function(err, result){
        // logic here
    });

    // delete instance
    instance.delete(function(err,result){
        // logic here
    });
});

Run a query

The following is a simple example of performing a query against a model.

Code Block
languagejs
// setup query options
var options = {
    where: { content: { $like: 'Hello%' } },
    sel: { content: 1 },
    order: { title: -1, content: 1 },
    limit: 3,
    skip: 0
};
// execute query
Model.query(options, function(err, coll) {
    // process results
});

...

Code Block
languagejs
// setup query options
var options = {
    content: { $like: 'Hello%' }
};
// execute query
Model.query(options, function(err, coll) {
    // process results
});

Restricting CRUD endpoints

By default, models support the basic CRUD methods (CREATE, READ, UPDATE, and DELETE). You can limit the methods supported by a model by using the actions property.

Code Block
languagejs
var Arrow = require('arrow');

var emp = Arrow.Model.reduce('appc.mysql/employee','emp',{
    fields: {
        fname: { type:String, description:'First name', name:'first_name', required:true},
        lname: { type:String, description:'Last name', required:true, name:'last_name'},
        email: { type:String, description:'Email address', required:true, name:'email_address'}
    },
    actions:['create','read'],
    connector: 'appc.mysql'
});

module.exports = emp;

In this example, the model only allows create (POST) or read (GET). DELETE and PUT are not allowed and would fail.

The valid values for the action property are: createreadupdatedelete, and deleteAll.

Predefined or custom endpoints

...

To disable API Builder from generating these endpoints, set the Model's autogen property to false when defining the model. You will need to create API create API Builder API objects to objects to access the model.

Example

The following model disabled generating pre-defined endpoints. An API endpoint needs to be defined to access the model data as shown below. 

...