fib-app
fibjs application basic api framework
Install
1npm install fib-app [--save]
Test
1npm 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
21const 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 person
is the Model definition module, as follows:
1
2
3
4
5
6
7module.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
4curl -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
, createdAt
individual fields will be automatically created and modified. createdBy
You 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
4curl -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:
1curl -X GET http://localhost/1.0/person/57fbbdb0a2400000
The body returns a JSON object contains all the user-supplied together with field 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"
}
Returns the field by providing keys
, customized content can be returned keys
content is in a ,
divided field name strings:
1curl -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
4curl -X PUT \
-H "Content-Type: application/json" \
-d '{"age": 25}' \
http://localhost/1.0/person/57fbbdb0a2400000
JSON returned object will contain updatedAt
and id
field 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:
1curl -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:
1curl -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 keys
custom field returns the result to contain. keys
The content is in a ,
field name string divided, for example:
1curl -X GET http://localhost/1.0/person?keys=name%2Cage
The specified return only name
and age
two fields.
where filter condition
By where
can make constraints on the query object as a parameter.
where
The 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 where
as 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:
1curl -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, where
comparison methods such as inclusion are also supported. where
The 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 skip
option, you can specify the number of records to skip, to achieve flip effect.
1curl -X GET http://localhost/1.0/person?skip=100
limit returns the record limit
By limit
option, you can limit the number of records returned, limit
the significant digits from 1 to 1000 and defaults to 100.
1curl -X GET http://localhost/1.0/person?limit=100
order specifies the sorting method
By order
option set to return Sort result set, before the field name contains -
as reverse time.
1curl -X GET http://localhost/1.0/person?order=-id
count returns the total number of results
When requested to increase count
the total number may return a result set of the specified content at the same time.
1curl -X GET http://localhost/1.0/person?count=1&limit=1
At this time, the return results containing count
and results
two 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
9module.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
4curl -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
4curl -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:
1curl -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
4curl -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:
1curl -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:
1curl -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
25const 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 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 role
and *
, id
represent a specific user, role
indicating 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 id
the corresponding rights, if not specified, the matching user role
permissions 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 user
user group can read, the user 57fbbdb0a2400000
has 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 true
to allow access, to false
prohibit access to array
allow only specified fields of access. delete
And find
does not accept array
, if you set array
then 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 user
only allows reading title
and detail
others 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
24module.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
41module.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 name
and 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:
1curl -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 svg
format class diagram, files will be saved to an image similar to the following: