MAPI is well created Web Server Framework. I noticed that developers need to consider lots of things when they started to create new services such that App, Web Service, Science Experimental, etc. And the services can be simply divided into Front-End and Back-End.
But actually, the idea is simply started from Front-End side, the UX like: 'How about creating a food delivery service? People can choose the foods what they want to eat, and the page will show the information about the food. Furthermore, when the order it, the seller will receive the infor where the location to deliver, and information they need.' It means that although the idea is started with User Experience(or User Interface, simply Frontend), developer must design and implement the BackEnd side.
So the concept of MAPI is 'Let the developers be able to just focus on the UX part. Let MAPI take responsibiliity for the Backend, so they can build differentiated, stand out service!'
So the MAPI borned. The MAPI provides DB Fetching, File Server, User Authentication with setting JSON files and process the features with RESTful API Calls.
All the requests to MAPI is processed through HTTP Methods.
MAPI is designed using the MVC pattern. Hence there are Model
, and Controller
but no View.
Controller is about the task what the response is made from for the request. The actions are defined as Service
.
For example you can define CRUD
task(here in after referred to as task) and HTTP Methods
correspondly. And when the request is coming to MAPI with one of defined HTTP Method
, the corresponding task will be processed.
The task is defined as Service
. Service
can define the corresponding request and task that is explained above. However, each HTTP Methods
can defined only once. It means, no duplicated HTTP Method
is allowed.
In summary, when http request is come, router will searching for corresponding Controller
. And then, Controller
will look for corresponding Service
. If there is any corresponding Service
and Controller
, the things will be processed, and return the result.
It's similiar with Dependency Injection
, and Inversion of Control
.
Router
->Controller
->Service
Router
<-Controller
<-Service
You have to set all the needed things JSON
file, according to its' right path under the configs
folder.
You should prepare configuration for MAPI. The base files are default.json
, model
, controller
, and service
.
configs
│ default.json
└─── firebase.json
└─── controller
│ └ rest
│ └─── [some_jsons_for_rest_setting.json]
└─── services
│ └ db
│ └─── [some_jsons_for_db_sql_setting.json]
│ └ function
│ └─── [some_jsons_for_function_setting.json]
└─── model
└─── [some_jsons_for_model_setting.json]
You need to set default setting for using MAPI.
Which setting is for using which controller, and the base-line setting for the controller.
For example, in REST API, you can set pre-fixed uri for requesting REST API. Database, you can set ip(host) of database, id, pw, port and scheme to use.
So the file is located right under configs/
, it will be configs/default.json
{
"cors": {
"default": "http://localhost:3000",
"origin": ["http://localhost:3000", "http://localhost:5000"],
"methods": "GET,PUT,POST,DELETE,HEAD,OPTIONS",
"allow-headers": "Authorization, Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin"
},
"restapi":{
"use": "yes",
"base-uri": "/",
"count": 10
},
"file-transfer":{
"use": "yes",
"base-uri": "/upload",
"read-uri": "file",
"base-directory": "/files",
"count": 10,
"type": "hostpath",
"table": "MAPI_FILE",
"columns": {
"parent-path": "parent",
"file-name": "file",
"timestamp": "timestamp",
"owner": "owner"
}
},
"database": {
"type": "mariadb|mongo",
"id": "DB_USER",
"pw": "DB_PW",
"host": "DB_IP",
"port": "DB_PORT",
"scheme": "DB_SCHEME|COLLECTION"
},
"jwt": {
"use": "yes",
"generate-uri": "/auth/do",
"verify-uri": "/auth/verify",
"lifetime": "600",
"secret": "JWT_SECRET",
"auth-table": "JWT_AUTH",
"auth-columns": ["name", "password"],
"alg": "HS256",
"columns": ["SEQ", "name", "email"],
"keys": {
"SEQ": "index",
"name": "nickname",
"email": "contact"
}
}
}
If mapi cannot connect to database, mapi will be shutdown with information such that Access Denied, Fail to connect.
If one of the property is missing, mapi will be shutdown with information about what went wrong.
cors property let MAPI allow cross-origin-servers to use the api.
default
: Define your base server address which use MAPI. If your front server or any others are operating in http://localhost:3000, then it will be your value. If you are operating MAPI in server-less environment, it will be hostname of MAPI.
origin
: Define cross-origins where is allowed to use MAPI. ["*"]
for wildcard.
methods
: Define methods that can be called to use MAPI.
allow-headers
: Default headers to use MAPI. You can add other headers, but should not remove any of it.
restapi property define essential information for REST API Controller (which for fetching data from database).
use
: Whether use REST API for fetching datas from database
. If no, the rest api properties & settings will be ignored. (Value: yes|no)
base-uri
: Define prefix uri to call rest-api protocol. If it is set to /mapi
and your rest api controller's uri have defined into /api/user
, the whole request uri will be /mapi/api/user
.
count
: Amount for pagination algorithm. REST API will fetch the amoutn of datas from database as defined. The key for query of pagination must be defined on REST API Properties
.
It is not supported currently. Will be updated later.
database property define Database connect information. If any of config is wrong, hence MAPI cannot connect to the database, MAPI will be shutdown with database error.
type
: Type of Database. Now only supports MariaDB, MongoDB, and Firestore. (Value: mariadb, mongo, firestore
)
id
: User id of Database
password
: User password of Database
host
: Hostname of Database
port
: Port of Database
scheme
: Database(scheme, collection) to use. It must be created in the Database.
If you want to use Firestore, add more firebase.json file in configs folder.
Firebase.json file is firebaseConifg file.
If you don't know firebaseConfig, check firebase project setting and find it.
If you can't access firestore, check Read-Write rules in firestore.
{
"apiKey": "apiKey",
"authDomain": "authDomain",
"databaseURL": "databaseURL",
"projectId": "projectId",
"storageBucket": " * If you use firebase storage,you need it * ",
"messagingSenderId": "messagingSenderId",
"appId": "appId",
"measurementId": "measurementId"
}
jwt property define Json Web Token settings. If your any controller require authorization, jwt will be used.
use
: Whether use JWT or not. If no, the jwt properties & settings will be ignored. (Value: yes|no)
generate-uri
: Uri to generate token. It must not be duplicated with any other API uri. You can generate token to the uri with POST method.
verify-uri
: Uri to verify given token. You must send token into request header, Authorization
field, value with Bearer token (Bearer eyJhbGciOiJIUzI1NiIsInR5cGUiOiJKV...
).
lifetime
: Lifetime of the generated token. If given token lived more than lifetime, you cannot authorize with the token. If so, you must get new token. (Unit: seconds)
secret
: Secret to create JWT Signature. IT MUST NOT BE EXPOSED.
auth-table
: Database table that must be created in RDB. JWT is issued based on user information, so this table will carry the users' information. It is used with auth-columns
, columns
, keys
properties. (Table is corresponded to Collection
in MongoDB
.)
alg
: Algorithm that is used to make JWT Signature.
columns
: Columns that generate JWT payload. These values are raw column name of RDB -> auth-table
. For security, this columns must not be EXPOSED. The columns' names will be refactor through keys
property. (Columns are corresponded to field
in MongoDB
.)
keys
: New key to refactor original columns
. For example, based on following properties, JWT payload will have number, nickname, contact
keys not id, name, email
. (Columns are corresponded to field
in MongoDB
.)
/* EXAMPLE */
"jwt": {
...
"columns": ["id", "name", "email"],
"keys": {
"id": "number",
"name": "nickname",
"email": "contact"
}
...
}
Controller is the same as I commented before. For now, it is on building level, I will update this docs later when it fixed.
I'm planning to create Controller which offers JWT, REST API for fetching Data from Database, FTP
. However, FTP and REST API for NoSQL DB will be considered and programming later.
Controller you need to
There are general properties for JSON files. (But not in default.json)
This properties are setting the controller what it would be.
id
: the unique string in same type. it is used to identify and call the controltype
: the type of control. it set what controller will run.auth
: the option requires authorization or not. need to set jwt before to use this option.proxy-list
: proxy method to use with this control. default proxies are:start, end, auth
.proxy-order
: when this method is called.log
: allow to log about the event or not.debug, warn, info, error
{
"id": "test",
"type": "api",
"auth": "no",
"proxy-list": ["start", "end"],
"proxy-order": 1,
"log": "debug",
}
There are more properties for REST API setting.
uri
: The uri to request this property.page-query
: The query key for using pagination. if the value ispage
, you can call rest api protocol with pagination request; /{base-uri}/{uri}/{id}?page=1page-count
: The query key for using pagination. if the value is10
, response will be include maximum 10 datas per each page. Deprecated, useconfigs/default.json -> count
instead.services
: The services that definingcrud task
forhttp request
. If there is no method defined for thehttp method
, it will return404 Not Found
. Also each services must be defined underconfigs/services/[db|function] -> one of json
.
The id of REST API controller could be duplicate, however, it should be identified by uri@id
.
For example, if the id is test
and uri is /api
, it's key will be /api@test
. It means, concatenation of uri@id
must be unique.
{
"uri": "/api",
"page-query": "page",
"page-count": 10,
"services": {
"get": {
"type": "db",
"id": "user_api",
"service": "read"
},
"post": {
"type": "db",
"id": "user_api",
"service": "create"
},
"put": {
"type": "db",
"id": "user_api",
"service": "update"
},
"delete": {
"type": "db",
"id": "user_api",
"service": "delete"
}
}
}
According to above json format, it infer that there is service named user_api
under configs/services/db/ one of json
. Note that, the id
of service doesn't mean the file name of json
is user_api.
Also the view can request with get, post, put, delete
http methods.
So total JSON file for REST API,
{
"id": "test",
"type": "api",
"auth": "no",
"proxy-list": ["start", "end"],
"proxy-order": 1,
"log": "debug",
"uri": "/",
"page-query": "page",
"page-count": 10,
"services": {
"get": {
"type": "db",
"id": "user_api",
"service": "read"
},
"post": {
"type": "db",
"id": "user_api",
"service": "create"
},
"put": {
"type": "db",
"id": "user_api",
"service": "update"
},
"delete": {
"type": "db",
"id": "user_api",
"service": "delete"
}
}
}
If the json file's name is test.json
, then the file must be exist on configs/controller/rest/test.json
(configs/controller/rest
is pre-fixed path).
Model is the data what you want to get, modify, and so on.
So the model is related with database. For now, mariadb, mongodb, firestore of Google
is supported.
It follows default knowledge with the controller.
There are more properties for Model setting.
id
: It is not same as general property, it is pointing table of database. it must be unique among the model setting. (Table is corresponded toCollection
inMongoDB
.)columns
: Columns to support Document DB for rest api. If your Collection uses any validation rule, it must be defined. support values:integer, long, float, double
. Other things you should put with stringify value (i.e, json, yaml, ...).ai-key
: It is one of column(field) for identifying the data. For RDB, it is name ofauto-increment
column, and Document DB, name of uuid column.
- Please be-care that, you must specify the data type for number,
long, double
in MongoDB if you use MongoDB Collection's validation. - In MongoDB, if you don't specify into right data type, it can bring about
Fail to Validation
Error.
{
"columns": {
"SEQ": "integer",
"NAME": "string",
"AGE": "long"
},
"ai-key": "SEQ"
}
So total JSON file for Model,
{
"id": "client",
"type": "model",
"auth": "no",
"proxy-list": [],
"log": "true",
"columns": {
"SEQ": "integer",
"NAME": "string",
"AGE": "long"
},
"ai-key": "SEQ"
}
If the json file's name is user.json
, then the file must be exist on configs/model/user.json
(configs/model
is pre-fixed path).
There are 2 type for the Service
. First one is about db sql, and another is about function. As explained above, the service is one of excutable things when the HTTP Request
is come.
Prefix path for Service json files is: configs/services
{
"id": "user_api",
"log": "yes",
"create": {
...
},
"read": {
...
},
"update": {
...
},
"delete": {
...
}
}
According to above json, user_api
service can process totally 4 CRUD tasks
.
create
: corresponding topost http method
read
: corresponding toget http method
update
: corresponding toput http method
delet
: corresponding todelete http method
db
is set of sqls. It consists of model
and query
.
-
model
: It is one ofModel
you defined inconfigs/model
. If it is not defined, when your service try to process the http request, error will be occured. -
query
: It describe sql to execute. query will have dynamic value capsuled with{{ }}
, for example{{ body.NAME }}
.query
can have 3 type of{{ }}
:{{ model }}
,{{ body. }}
, and{{ condition. }}
{{ model }}
is pointing right abovemodel
property.{{ body. }}
is corresponding to body parameter. For example,{{ body.NAME }}
, http request must include NAME field on body parameter. If body parameter includeNAME: Eugene
,{{ body.NAME }}
will be changed intoEugene
.{{ condition. }}
is corresponding to URI segment. For example,{{ condition.NAME }}
, http uri must looks like:/{uri_to_request_api}/NAME/Eugene
. It means,{{ condition.NAME }}
will be changed intoEugene
If you defined sql with {{ }}
option, however, your request doesn't include it, it will occur error.
{
"id": "user_api",
"log": "yes",
"create": {
"model": "client",
"query": "INSERT INTO {{ model }} ( NAME, AGE, DESCRIPTION ) VALUES ( {{ body.NAME }}, {{ body.AGE }}, 'hello' )"
},
"read": {
"model": "client",
"query": "SELECT * FROM {{ model }}"
},
"update": {
"model": "client",
"query": "UPDATE {{ model }} SET NAME = {{ body.NAME }}, AGE = {{ body.AGE }} WHERE SEQ = {{ condition.SEQ }}"
},
"delete": {
"model": "client",
"query": "DELETE FROM {{ model }} WHERE SEQ = {{ body.SEQ }}"
}
}
{
"id": "user_api_mongo",
"log": "yes",
"create": {
"model": "client",
"query": "{ 'NAME': {{ body.NAME }}, 'AGE': {{ body.AGE }} }"
},
"read": {
"model": "client",
"query": ""
},
"update": {
"model": "client",
"query": "{ 'NAME': {{ body.NAME }}, 'AGE': {{ body.AGE }} }"
},
"delete": {
"model": "client",
"query": "{ 'NAME': {{ body.NAME }} }"
}
}
File Transfer is not supported currently. Will be updated later.
Uri to request model information, you need 3 things from configuration.
default.json
:restapi['base-uri']
controller.rest.*.json
:uri
,id
You need to concatenate both things, so the result uri would be /restapi['base-uri']/
controller.rest..json.uri/
controller.rest..json.id`.
For example, with the below configuration, you can request to /api/user
.
# default.json
{
...
"restapi": {
"use": "yes",
"base-uri": "/",
"count": 10
},
...
}
# controller/rest/user.json
{
...
"id": "user",
"uri": "/api",
...
}
You may want to manipulate database's model, and here is a solution to select, insert, update, delete
methods (get, create, update, remove
). I will update this article with details later.
GET
: Method to select
data from database. The result will be selected based on model configuration. If succeed, http response code will be 200 or 204.
POST
: Method to insert
data to database. Http response code will be 201 or 200. (201: created, 200: already exists)
PUT
: Method to update
data of database. You must specify exist data to modify with URI segments to specify it. Http response code will be 200, 201. (200: updated, 201: created)
DELETE
: Method to delete
data from database. You must specify exist data to modify in body parameter. Http response code will be 204 whether deleted or not.
OPTIONS
: not supported yet.
HEAD
: not supported yet.
The uri for JWT is set on default.json -> "jwt" property
. You need generate-uri
and verify-uri
from the jwt
property.
configs
│ default.json
└─── controller
│ └ rest
│ └─── [some_jsons_for_rest_setting.json]
└─── services
│ └ db
│ └─── [some_jsons_for_db_sql_setting.json]
│ └ function
│ └─── [some_jsons_for_function_setting.json]
└─── model
└─── [some_jsons_for_model_setting.json]
If you finished to set the Prerequisites
files such that controller and model in some location with name /path/to/configs/
(ex. /home/mapi/configs
), you can run with following docker commands
~$ mkdir -p mapi/configs
# Copy Prerequisites configuration into configs
~$ cd mapi
mapi$ cp -r [/path/to/pre-configs/] ./configs
~$ docker run -d -p 3000:[PORT] -v ./configs:/app/configs --name mapi devwhoan/mapi:0.0.6
- The developed environment uses
Nodejs 16.16.0
If you want to modify and change some codes or whatever, download the code, and just run the nodejs.
As I commented uphead, you need to set Prerequisites
for running the node process.
~$ git clone https://github.com/dev-whoan/mapi.git
~$ cd mapi
# Copy Prerequisites configuration into configs
mapi$ mkdir configs
mapi$ cp -r [/path/to/pre-configs/] ./configs
mapi$ npm install
mapi$ npm start