Awesome Community Module

fib-app

fibjs application basic api framework

Install

1
npm install fib-app [--save]

Test

1
npm test

Establish 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();

Where person is the Model definition module, the content is 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 can also use other functions of orm, 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 a request failure. When a request fails, the body of the response is still a JSON object, but it always contains two fields, code and message, which you can use to debug. For example, if a request for authorization 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 403 indicate the error type, 05 indicates the data table number, and 01 indicates the detailed error code.

For GET requests, object data is usually returned. Depending on the address of the GET request, an object or an array may be returned. such as:

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

There are four fields with special meanings in the object data, which are not allowed to be changed through the API. They are id , updatedAt , createdAt , createdBy .

The id , updatedAt , createdAt individual fields will be automatically created and modified. createdBy needs to specify its own type.

Basic object access API

After completing such data definition, you will directly have a 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 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 described 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 that contains 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 contents 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 returned is a JSON object containing all user-supplied fields plus the createdAt , updatedAt and id fields:

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" }

By setting field returns keys , customized content can be returned, keys to a content to , segmentation 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 object

In order to change the data that an object already has, you can send a PUT request to the corresponding URL of the object. Any key you do n’t specify will not change, 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

The returned JSON object will contain updatedAt and id fields, indicating when the update occurred:

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

Delete object

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

You can get multiple objects at once by sending a GET request to the URL of the class without any URL parameters. Here is a simple way 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" } ]

Key field customization

As with object queries, you can set the keys to customize the fields included in the returned results when querying the list. keys of a content to , segmentation field name strings, for example:

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

Will specify that only two fields name and age are returned.

where filter

The query object can be constrained by the form of the where parameter.

The value of the where parameter should be JSON encoded. That is, if you look at the URL request that was actually issued, it should be encoded in JSON first, and then encoded in the URL. The easiest way to use the where parameter is to include the required key and value. For example, if we want to search for a user named tom, we should construct a query like this:

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

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

In addition to exactly matching a given value, where also supports comparison methods, such as inclusion. where parameter supports 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

Through skip option, you can skip the specified number of records to achieve the effect of turning pages.

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

limit returns the record limit

The limit option can limit the number of records returned. The valid number of limit is 1-1000, and the default is 100.

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

order specifies the ordering method

With the order option, you can set the sorting method of the returned result set. When the field name contains- - it is in reverse order.

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

count returns the total number of results

Increasing count when requesting can return the total number of result sets while returning the specified content.

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

At this time, the returned result will contain two fields, count and results , which contain the total number and the result, respectively:

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 extended objects

By defining hasOne and hasMany through orm, the 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 extended object
/1.0/:className/:id/:extendName POST Create extension object
/1.0/:className/:id/:extendName/:rid GET Read extended objects
/1.0/:className/:id/:extendName/:rid PUT Modify extended objects
/1.0/:className/:id/:extendName/:rid DELETE Delete extended object
/1.0/:className/:id/:extendName GET Query extended object list

Set extended 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 extension object

Direct creation of extended objects allows you to establish connections between objects while creating them. such as:

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 will be associated with tom.

Read extended objects

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

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

Modify extended objects

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 extended object

Deleting an extended object does not delete the object itself, it only releases the relationship between the objects:

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

Query extended object list

The query extended object list is very similar to the query 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

You can control the data permissions by defining the ACL of the Model. such as:

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: String, note: String }, { ACL: function(session) { return { "*": { "*": false }, "57fbbdb0a2400000": { "*": true }, "roles": { "user": { "read": true } } }; } }); };

If the ACL is not specified when defining the Model, it is equivalent to setting the default permissions:

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

main body

There are three types of ACL body description, user id , user role and * , id represents a specific user, role represents a user with a certain role, * represents all users:

main body description priority
id Specific user id 1
role User group name 2
* All 3

When checking permissions, you will first match id permissions correspond, if not specified, matching user role corresponding authority, if still specified, it looks at whether the * authority, if * is also not specified, do not have permission.

For example, the above permission configuration specifies that the user user group can be read. User 57fbbdb0a2400000 has all permissions, while other users do not have any permissions.

Authority

ACL classifies permissions into five categories based on API behavior:

Authority description Allowed types
create Create Object true / false / array
read Read object true / false / array
write Modify object true / false / array
delete Delete object true / false
find Query object list true / false
* Match all permissions true / false / array

The permissions are set to true to allow access, false to prohibit access, and array to only allow access to specified fields. delete and find do not accept array , if array is set, it is regarded as true . If the specified permission does not exist, it matches the * permission under the same subject. If none exist, query the subject of the next priority again.

For example, in the above example, if you need to set the user only read title and detail , and others can read the title , you can set it like this:

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 on 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 access is prohibited. The permissions will be checked according to the following steps:

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

Extended object permissions

The access rights control of extended objects is similar to the basic object rights, except that the ACL needs to be specified separately:

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); };

In this definition, anyone can check the name and sex personal information, and freely check and search his pets . The user himself can operate all his own data and have all the permissions of his own pet information.

When checking the access rights of extended objects, the object rights and extended object rights 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 Models. For complex data operations, custom Functions can be used to complete.

The vast majority of permissions can be completed through ACL control, without the need to complete object-based permissions through Function. Function can be used to complete data-based permissions, such as granting permissions to different user groups based on approval status. And many modifications, such as the need to modify multiple database records.

Plot the data model

After completing the data definition, you can use app.diagram() draw the svg format class diagram of the data model, and save it to a file to get an image similar to the following: diagram