# Module Rest-API
API-bxcore is a minimalistic and flexible framework
that provides an extensive set of functions for building REST APIs on Node.js.

It has at its disposal numerous utility methods and
middleware handlers,
for creating a reliable professional server quickly and easily.

This module provides a web panel for viewing,
testing and analyzing API methods.

### Install module
```javascript
npm i api-bxcore
```

#### Quick start
```javascript
const express = require('express');
const app = express();

const RestAPI = require('api-bxcore');
const restapi = new RestAPI({}, {
    auth_user: (user) => {

        return new Promise(resolve => {
            if (user.session.auth && user.session.user_id)
                resolve({status: true, user_id: user.session.user_id});
            else
                resolve({status: false})

        });
    },
    auth_admin: (user) => {
        return new Promise(resolve => {
             if (user.session.auth && user.session.user_id&& user.session.admin ===1)
                 resolve({status: true, user_id: user.session.user_id});
             else
                 resolve({status: false})
        });
    }
});

app.use('/api/v1', restapi.expressRouter);
```
- need to describe the basic principles of operation
- describe what the auth_user auth_admin functions are
- describe how a configuration file is created
- describe what api express router is
- describe configuration file what each parameter means in it
## Request parameters checksum for restAPI authorization
```
    let param = {...__GET, ...__POST};
    const checksumParams = crypto.createHash('sha256')
        .update(JSON.stringify(param))
        .digest('hex');
        
```
API Types and validate
----
- ##### String:
```
/**
* @param {string} options.regexp - STRING REGEXP (optional)
*/
API.types.STRING(lenght,options)
```               
- ##### Number:
```
/**
*      ┌──────────────── fixed (3)
*  13.556 ───────── length (5)

* @param {string} options.regexp - STRING REGEXP (optional)
*/
API.types.FLOAT(length,fixed,options) //float number
API.types.INTEGER(length,options) // integer
```          
- ##### Date:
```
/**
* @param {number} options.zone_utc - Convert to ZONE (example 0 = UTC) (optional)
*/
API.types.DATE(options)
```

- ##### Enum:
```
API.types.ENUM(...values) 
// example API.types.ENUM('active','pending','deleted') 
```

- ##### Boolean:
```
API.types.BOOLEAN()
```

- ##### Object:
```
API.types.OBJECT()
```

- ##### Array:
```
API.types.ARRAY()
```

- ##### FILE:  (validator soon)
```
API.types.FILE()
```

#### Own type
You can also create your own type.
for this you need to add the file to the ```PROJECT_ROOT/api_plugins``` folder
###### Example
```
// PROJECT_ROOT/api_plugins/object-id-type.js

const mongoose = require('mongoose');

module.exports = (API) => {

    class _ObjectId {
        constructor(len, options) {
            this.name = 'ObjectID';

        }

        valid(v) {
            if (!v || v === '' || typeof v !== 'string' || !mongoose.Types.ObjectId.isValid(v))
                return {
                    success: false,
                    error: 'Value is not ObjectId'
                };
            return {success: true, value: v};
        }
    }

    API.types.ObjectId = () => {
        return new _ObjectId();
    };
};

//Example use type 
API.types.ObjectId()
```



### API File example
./api/GROUP_PATH/API_file.js
```javascript
module.exports = (API, redis) => {
    // method api/v1/GROUP_PATH/API_file/test
    API.register('test', async (user, param) => {
       return {ok:1};
    }, {
        title: 'Test API',
        level: 0,// 0 public,1 user,2 admin,3 server
        description: 'This method for test and learn api. =)',
        group: "Testing API",
        param: {
            test:{
                type: API.types.STRING(10),
                error_code:12345678,
                title: 'example param',
                required: true
            }
        },
        response: [{
            name: 'data.example',
            type: "string",
            title: 'example res',
            default: '1'
        }]
    });
};
```
Each register must contain a function with return Promise - it's important that each method returns a promise as a result of execution
How is the method name generated?
api/v1/ + path to file with method + (1st parameter from register if it's a string)
thus we get
api/v1/folder1/folder2/file/test
##### Error handling
Errors have a strict structure - all API responses return a response in JSON format:
###### Example error response:

       {
         "success": false,
         "error": {
           "apiError": true,
           "message": "Page not found",
           "errorType": "api",
           "object": {
             
           },
           "level": 0,
           "errorCode": 4041543664702404,
           "stack": "Object.API.register [as fn] (./path/api/file.js:444:52)"
         },
         "latency_ms": 4,
         "requestId": "1543748408108-wirUSou"
       }

Often we need to describe our own errors in methods so that our client understands what went wrong. The API has a built-in convenient and flexible error creation system:
###### Generating errors in methods:
You just need to use this code in your method: (the method will immediately return ***Promise.reject***)
```
    return API.error.response("Error message example",55511111) 
    
```
this method will work in most cases - it takes 3 parameters (message,errorcode,params) - parameters are not required but if your method should return something additionally you can use it.

More flexible way to describe errors:
```
    return Promise.reject(
        API.error.create("Error message example",'api',{},10,55511111,500)
    )
```
This method takes parameters type and error level, as well as statusCode with which the request will be returned
parameters of this method (message, type, params, level, errorCode, statusCode)
***Be careful as this method does not return reject in itself - it only creates the needed object for its return - API you need to return its response in Promise.reject()***


##### Each method has documentation in which
- title - method title
- level - method access level

      0 - public method, authorization not required
      
      1 - user method, before calling the auth_user function will be called
          which should return status: true
          if user is authorized and can also return additionally user_id
          so that the method has the parameter auth.user.user_id
          
      2 - Admin method similar to user method
          auth_admin verification function
          
      3 - Server method can only be called on server
          (for example in other methods) API.call($method,$user,$params,'server')

- description - method description (details, warnings and other information about the method)
- group - method group (category to which the method belongs for documentation to conveniently format documentation)
- hide - don't show this method in the list (documentation) (secret method) (optional)
- param - parameters

        paramName: {
            type: API.types.STRING(10), // parameter type
            error_code:12345678, // error code if parameter comes invalid
            title: 'example param', // description
            required: true // required
        }
        
        // parameters can also be an array
        paramArray: [{
             type: API.types.STRING(10), // parameter type
             error_code:12345678, // error code if parameter comes invalid
             title: 'example param', // description
             required: true // required
        }] 
        // in this case each array element will be checked for type compliance
           and if required is set then the array must have at least one element
           
        // parameter can also be an array with objects
           in this case the entire array with needed objects will be checked
           
            paramArray: [{param1:{type...},param2:{type...}}
- response - array that describes what the method will return


based on this documentation all parameters will be validated and access levels before the parameter itself is called
if a syntax error occurs in the method, the method will return code 500 with error indication



After starting the application, the ./api/ and ./config/ folder will be created.
- `./api/` - directory with API
- `./config/api_config.json` - configuration file


configuration file is created on first run - proper configuration affects API operation
all parameters have logical names - you can familiarize yourself with it independently


## Plugins (./api_plugins)

- DDOS/Flood protect

  **How to use:**
  ```
   const ddosKey = 'protect-1'; // key for count req
   const floodSign = await API.plugin.ddos.lockApi(auth.ddosKey, blockForSec);
   await API.plugin.ddos.checkLockApi(auth.ddosKey, floodSign, delayMS);
  ```
___
- Param types ObjectId

  **How to use:**
  ```
  param:{
    newsId: {
      type: API.types.ObjectId(),
      // ...
    }
  }
  ```
___
- Quick param pagination API.method

  ```
  // in source validator
  let page = API.plugin.pageAPI.get(param.page, param.limit, {maxLimit: 100, defaultLimit: 20, maxPage: 2000});
  
  
  // in meta block
  param: {
    ...API.plugin.pageAPI.docsParams(API),
  } //...
  ```
___