Awesome community module

fib-app

fibjs application basic api framework

Install

1
npm install fib-app [--save]

Test

1
npm test

Build a basic script

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
const http = require('http'); const util = require('util') const Session = require('fib-session') const App = require('../'); var app = new App('sqlite:test.db', { uuid: true }); app.db.use(require('./defs/person')); var session = new Session(new util.LruCache(20000), { timeout: 60 * 1000 }); var svr = new http.Server(8080, [ session.cookie_filter, { '/1.0': app } ]); svr.run();

Wherein personis the Model definition module, as follows:

1 2 3 4 5 6 7
module.exports = db => { db.define('person', { name: String, sex: ["male", "female"], age: Number }); };

This is a standard orm definition, and other functions of orm can also be used, such as type checking, events, etc.

API data format

For POST and PUT requests, the body of the request must be in JSON format, and the Content-Type of the HTTP header needs to be set to application/json.

1 2 3 4
curl -X PUT \ -H "Content-Type: application/json" \ -d '{"name": "tom","sex":"male","age":23}' \ http://localhost/1.0/person/57fbbdb0a2400000

For all requests, the response format is a JSON object.

The success of a request is indicated by the HTTP status code. A 2XX status code indicates success, and a 4XX indicates failure of the request. When a request fails, the body of the response is still a JSON object, but it will always contain the two fields of code and message, which you can use for debugging. For example, if a request for permission authentication fails, the following information will be returned:

1 2 3 4
{ "code": 4030501, "message": "The operation isn’t allowed for clients due to class-level permissions." }

The code is divided into three parts. The first three digits 403 indicate the error type, 05 indicate the data table number, and 01 indicate the detailed error code.

For a GET request, the object data is usually returned. Depending on the address of the GET request, an object or an array may be returned. for example:

1 2 3 4 5
{ "name": "tom", "sex": "male", "age": 23 }

or:

1 2 3 4 5 6 7 8 9 10 11 12
[ { "name": "tom", "sex": "male", "age": 23 }, { "name": "lily", "sex": "female", "age": 22 } ]

Special field

In the object data, there are four fields with special meanings, which are not allowed to be changed through the API. Respectively id, updatedAt, createdAt, createdBy.

Where id, updatedAt, createdAtindividual fields will be automatically created and modified. createdByYou need to specify the type yourself.

Basic object access API

After completing this data definition, you will directly have a complete set of interface calls that conform to the REST api specification:

url method action
/1.0/:className POST Create new object
/1.0/:className/:id GET Read object
/1.0/:className/:id PUT Modify the object
/1.0/:className/:id DELETE Delete object
/1.0/:className GET Query object list

Create new object

In order to create a new object, a POST request should be sent to the URL of the class, which should contain the object itself. For example, to create the object as mentioned above:

1 2 3 4
curl -X POST \ -H "Content-Type: application/json" \ -d '{"name": "tom","sex":"male","age":23}' \ http://localhost/1.0/person

When the creation is successful, the HTTP response is 201 Created, and the body of the response is a JSON object, including the objectId and createdAt timestamp of the new object:

1 2 3 4
{ "createdAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400000" }

Read object

When you create an object, you can get its content by sending a GET request to the Location of the returned header. For example, to get the object we created above:

1
curl -X GET http://localhost/1.0/person/57fbbdb0a2400000

The body returns a JSON object contains all the user-supplied together with field createdAt, updatedAtand idfields:

1 2 3 4 5 6 7 8
{ "name": "tom", "sex": "male", "age": 23, "createdAt": "2017-11-25T01:39:35.931Z", "updatedAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400000" }

Returns the field by providing keys, customized content can be returned keyscontent is in a ,divided field name strings:

1
curl -X GET http://localhost/1.0/person/57fbbdb0a2400000?keys=name%2Csex

Will return:

1 2 3 4
{ "name": "tom", "sex": "male" }

Modify the object

In order to change the existing data of an object, you can send a PUT request to the corresponding URL of the object. Any key you have not specified will not be changed, so you can only update a subset of the object data. For example, let's change an age field of our object:

1 2 3 4
curl -X PUT \ -H "Content-Type: application/json" \ -d '{"age": 25}' \ http://localhost/1.0/person/57fbbdb0a2400000

JSON returned object will contain updatedAtand idfield indicating the update occurred at a time:

1 2 3 4
{ "updatedAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400000" }

Delete object

In order to delete an object, you can send a DELETE request to the URL of the specified object, such as:

1
curl -X DELETE http://localhost/1.0/person/57fbbdb0a2400000

Query object list

By sending a GET request to the URL of the class, you can get multiple objects at once without any URL parameters. The following is simply to get all users:

1
curl -X GET http://localhost/1.0/person

The returned value is a JSON object containing the results field, and its value is a list of objects:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
[ { "name": "tom", "sex": "male", "age": 23, "createdAt": "2017-11-25T01:39:35.931Z", "updatedAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400000" }, { "name": "lily", "sex": "female", "age": 22, "createdAt": "2017-11-25T01:39:35.931Z", "updatedAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400001" } ]

keys field customization

As with object query, you can set the query list keyscustom field returns the result to contain. keysThe content is in a ,field name string divided, for example:

1
curl -X GET http://localhost/1.0/person?keys=name%2Cage

The specified return only nameand agetwo fields.

where filter condition

By wherecan make constraints on the query object as a parameter.

whereThe value of the parameter should be JSON-encoded. In other words, if you look at the URL request that was actually sent, it should be JSON-encoded first, and then URL-encoded. The easiest to use whereas arguments is to include proper key and value. For example, if we want to search for users whose name is tom, we should construct the query like this:

1
curl -X GET http://localhost/1.0/person?where=%7B%22name%22%3A%22tom%22%7D

where The value is a JSON string after urlencode, the content is:{"name":"tom"}

In addition to exactly matching a given value, wherecomparison methods such as inclusion are also supported. whereThe parameters support the following options:

key operation sample
eq equal {"name":{"eq":"tom"}} or {"name":"tom"}
ne not equal to {"name":{"ne":"tom"}}
gt more than the {"age":{"gt":"24"}}
gte greater or equal to {"age":{"gte":"24"}}
lt Less than {"age":{"lt":"24"}}
lte Less than or equal to {"age":{"lte":"24"}}
like Fuzzy query {"name":{"like":"%m"}}
not_like Fuzzy query {"name":{"not_like":"%m"}}
between Interval comparison {"age":{"between":[22,25]}}
not_between Interval comparison {"age":{"not_between":[22,25]}}
in enumerate {"name":{"in":["tom","lily"]}}
not_in enumerate {"name":{"not_in":["tom","lily"]}}
or OR operation {"or":[{"name":"tom"},{"age":24}]}

skip skip records

By skipoption, you can specify the number of records to skip, to achieve flip effect.

1
curl -X GET http://localhost/1.0/person?skip=100

limit returns the record limit

By limitoption, you can limit the number of records returned, limitthe significant digits from 1 to 1000 and defaults to 100.

1
curl -X GET http://localhost/1.0/person?limit=100

order specifies the sorting method

By orderoption set to return Sort result set, before the field name contains -as reverse time.

1
curl -X GET http://localhost/1.0/person?order=-id

count returns the total number of results

When requested to increase countthe total number may return a result set of the specified content at the same time.

1
curl -X GET http://localhost/1.0/person?count=1&limit=1

At this time, the return results containing countand resultstwo fields, and each comprising a total number of results:

1 2 3 4 5 6 7 8 9 10 11 12 13
{ "count": 2, "results": [ { "name": "tom", "sex": "male", "age": 23, "createdAt": "2017-11-25T01:39:35.931Z", "updatedAt": "2017-11-25T01:39:35.931Z", "id": "57fbbdb0a2400000" } ] }

Create an extension object

By defining hasOne and hasMany through orm, the association relationship between objects can be defined and reflected in the API, for example:

1 2 3 4 5 6 7 8 9
module.exports = db => { var Person = db.models.person; var Pet = db.define('pet', { name: String }); Person.hasMany('pets', Pet); };

Extended object access API

The following is the API definition of the extension object:

url method action
/1.0/:className/:id/:extendName PUT Set extension object
/1.0/:className/:id/:extendName POST Create an extension object
/1.0/:className/:id/:extendName/:rid GET Read extended object
/1.0/:className/:id/:extendName/:rid PUT Modify the extension object
/1.0/:className/:id/:extendName/:rid DELETE Delete extension object
/1.0/:className/:id/:extendName GET Query the list of extended objects

Set extension object

Setting an extended object is to establish a connection between two independent objects. For example, Tom has adopted a pet named cat, which can be achieved with the following operations:

1 2 3 4
curl -X PUT \ -H "Content-Type: application/json" \ -d '{"id": "57fbbdb0a2400007"}' \ http://localhost/1.0/person/57fbbdb0a2400000/pets

In the call, the id of cat needs to be specified in the body.

Create an extension object

By directly creating extended objects, you can establish connections between objects while creating objects. for example:

1 2 3 4
curl -X POST \ -H "Content-Type: application/json" \ -d '{"name": "cat"}' \ http://localhost/1.0/person/57fbbdb0a2400000/pets

A pet named cat will be created and an association relationship with tom will be established.

Read extended object

Reading extended objects is very similar to reading base objects, and also supports the keys option:

1
curl -X GET http://localhost/1.0/person/57fbbdb0a2400000/pets/57fbbdb0a2400007

Modify the extension object

Reading extended objects is very similar to reading base objects:

1 2 3 4
curl -X PUT \ -H "Content-Type: application/json" \ -d '{"name": "cat 1"}' \ http://localhost/1.0/person/57fbbdb0a2400000/pets/57fbbdb0a2400007

Delete extension object

Deleting an extended object does not delete the object itself, but only removes the relationship between the objects:

1
curl -X DETELE http://localhost/1.0/person/57fbbdb0a2400000/pets/57fbbdb0a2400007

Query the list of extended objects

Querying the extended object list is very similar to querying the basic object list, and also supports options such as keys and conditional filtering:

1
curl -X GET http://localhost/1.0/person/57fbbdb0a2400000/pets

ACL

The data permissions can be controlled by defining the ACL of the Model. for example:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
const orm = require('fib-orm'); module.exports = db => { db.define('blog', { title: String, detail: Stringnote: String }, { ACL: function(session) { return { "*": { "*": false }, "57fbbdb0a2400000": { "*": true }, "roles": { "user": { "read": true } } }; } }); };

If no ACL is specified when the Model is defined, it is equivalent to setting the default permissions:

1 2 3 4 5
{ "*": { "*": true } }

main body

There are three main ACL description, user id, user roleand *, idrepresent a specific user, roleindicating that the user has a role, *means that all users:

main body describe priority
id The id of the specific user 1
role User group name 2
* All 3

When checking privileges, the first will match idthe corresponding rights, if not specified, the matching user rolepermissions corresponding still if specified, to see if the designated *authority, if *also not specified, do not have permission.

For example, the above permission configuration, specify the useruser group can read, the user 57fbbdb0a2400000has full rights, while other users without any permission.

Authority

ACL categorizes five types of permissions based on API behavior:

Authority describe Allowed type
create Create an object true / false / array
read Read object true / false / array
write Modify the object true / false / array
delete Delete object true / false
find Query object list true / false
* Match all permissions true / false / array

Permissions developed trueto allow access, to falseprohibit access to arrayallow only specified fields of access. deleteAnd finddoes not accept array, if you set arraythen regarded as true. If the specified permission does not exist, then the next match with the main *authority. If none exists, query the subject of the next priority again.

Examples of the above example, if required to set useronly allows reading titleand detailothers can be read title, it can be set such that:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
{ "*": { "*": false, "read": ['title'] }, "57fbbdb0a2400000": { "*": true }, "roles": { "user": { "read": ['title', 'detail'] } } }

Object permissions

The permissions of the entire class are set on the Model. If you need to set permissions for specific objects, you can set OACL to achieve:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
module.exports = db => { db.define('person', { name: String, sex: ["male", "female"], age: Number }, { ACL: function(session) { return { "*": { "*": false } } }, OACL: function(session) { var _acl = {}; if(this.id === session.id) _acl[session.id] = { "*": true }; return _acl; } }); };

In this example, when the visitor is the subject, all operations will be allowed, otherwise all visits will be prohibited. The permissions will be checked according to the following steps:

  • person[57fbbdb0a2400000] => OACL
  • person => ACL

Extended object permissions

The access permission control of the extended object is similar to the basic object permission, the only difference is that it needs to be specified separately in the ACL:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
module.exports = db => { var Person = db.define('person', { name: String, sex: ["male", "female"], age: Number },{ ACL: function(session) { return { "*": { "read": ['name', 'sex'], "extends": { "pets": { "read": true, "find": true } } } } }, OACL: function(session) { var _acl = {}; if(this.id === session.id) _acl[session.id] = { "*": true, "extends": { "pets": { "*": true } } }; return _acl; } }); var Pet = db.define('pet', { name: String }); Person.hasMany('pets', Pet); };

This definition, anyone can access to personal information nameand sex, and free access and search him pets, I am the user can operate all of their data, and all rights to have their own pet information.

When checking the access authority of the extended object, the object authority and the extended object authority are checked separately. For example, the following request:

1
curl -X GET http://localhost/1.0/person/57fbbdb0a2400000/pets/57fbbdb0a2400007

The permissions will be checked according to the following steps:

  • pets[57fbbdb0a2400007] => OACL
  • person[57fbbdb0a2400000]=> OACL=> extends=>pets
  • person=> ACL=> extends=>pets
  • pets => ACL

Function

APIs can be defined for the Model, and complex data operations can be completed by customizing the Function.

The vast majority of permissions can be controlled by ACL, and no function is required to complete object-based permissions. Function can be used to complete data-based permissions, such as granting permissions to different user groups based on approval status. And multiple modifications, such as the need to modify multiple database records.

Draw the data model

After completion of the data definition may be used app.diagram()to draw data model svgformat class diagram, files will be saved to an image similar to the following: diagram