Handling APIs

In spoova, APIs are window routes that are declared as api channel. Since web urls are resolved using window route classes, APIs are routes specifically declared as API through a top level API integration. This means that they can inherit all the properties and functionalities of a normal route. A route can be declared as API by either calling the integrateAPI() method or by declaring the $winAPI static property using specifically designed options. When a route is declared as API, the effect is usually being felt by the route shutter methods which have the internal capacity to convert ordinary routes to APIs. Shutter methods which are rootcall(), pathcall(), basecall() and call() methods are designed to detect the response of a route whether it is a normal webpage or an API channel.

API Integeration
The figure below explains how to integerate an API with any window page
Syntax: integerateAPI
  Window::integerateAPI($type)  
  
    where: 

     $type : [ajax|json|ajax:json|json:ajax]
  
                                    
The integerateAPI() method defines that a route should respond as an API channel while $type above defines the format or reaction of response obtained.

Example: integerateAPI
  ...
  
  class HomeAPI {

    function __construct() {

        self::integerateAPI('ajax');

    }

  }
                                    
The example above is a format of how to set up an api window url by using the self::integerateAPI() method. This method must be called prior to the use of shutter methods. There are three different response types which are ajax, json and ajax:json or json:ajax. The behavioral pattern or how these types respond to shutter methods are further explained below under their own subheadings.

AJAX Response Type
Whenever the integerateAPI() is set as ajax, if a url does not exist, rather than return a 404 request page, the shutters will automatically switch to a json format 404 response type using the format below:
Invalid url response on integerateAPI('ajax')
  {
    "success":false,

    "error":true,

    "message":"Page not found!",

    "response_code":404
  }
    
The integerateAPI('ajax') does not really check if the response of any window content matches a json format. The header('content-type') does not specify this. It only believes that if an error occurs, then a json format should be returned as a response.

JSON Respose Type
When integrateAPI() is set as json, this will declare that the content of a page should be of json format. Hence the header content-type is set as application/json. If the content-type is not of a valid json content type, the response returned will be json SyntaxError notifying that the content cannot be parsed as revealed in the figure below:
Figure 1: Json Parse Error
The FIGURE 1 above is an example of a response returned by an api json-integerated window if the response type is not of valid json format.
Figure 2: Json 404 Error
The FIGURE 2 above is an example of a response returned by an api json-integerated window if url is not valid or the method does not exist.

AJAX:JSON Response Type
The ajax:json or json:ajax response type is more of a true request validation type on json content-type. When integerateAPI('ajax:json') is set on any window, the content type of that window must be a json format just like integerateAPI('json'). Secondly, the request type must also be through an ajax request type. The validation execution order will return a 401 request if the request is not sent through ajax first. However, if the request is sent through ajax, then the validation will go on to check if the content type is of json format also. The figure below shows the response returned if the requested page is loaded directly and not through ajax request.
Figure 2: Json 404 Error
The example above best reveals an error response obtained when an integerateAPI('json:ajax') is loaded directly.

Modifying Responses

The integerateAPI() method supports more than one argument. There are certain cases developers may want to modify the default response message of the json:ajax Api when the request method is not valid. This can be supplied through a second argument. An example of this is shown below

  self::integrateAPI('ajax:json', 'invalid request protocol'). 
                                      
The response default error code "401" can also be modified through a third argument. An example is shown below
integrateAPI('ajax:json', 'invalid request protocol', 402)
                                      
It is important to learn and have a good understanding of the Ajax class from here in order to be able to work easily with authenticating request APIs. Whenever a page returns 404, the integerateAPI will always return a response shown in Figure 1 earlier . There are other means to set up api routes without using shutter. This process involves the use of Ajax class and response() function to validate route responses. The example below best explains this process.

Example: Custom APIs
  class APIs {

    function __construct(){

        self::integerateAPI('ajax'); // set an ajax channel

        Ajax::accept('post'); // accept only post requests 
        
        Ajax::accept('post')->referred(); // accept only posts requests and it must be referred
        
        Ajax::with('json')->referred(); // accept only posts requests and it must referred with response returned in json format 
        
        Ajax::accept(['post','get'])->with('json')->referred(); // accept only posts and get requests and it must referred and returned value must be of json format
        
        if(Ajax::isAjax()){

            //If this request is an Ajax request, run this block code

            return response(404, 'message here');

        } else {

            //If this request is not an Ajax, run this block code

            return response(404, 'message here');         

        }


    }

  }
                                    
The example above shows different methods of setting an api route. We suggest to visit the ajax documentation to understand how the Ajax class works. The window pattern is structurally designed in a way that api can be built on urls whose parent url are not of ajax or json types. An example below shows how this can be achieved.

Example 2: Building API on routes
  class Home {

    function __construct(){

        self::call(
            [
                window(':') => 'root',    
                window(':user') => 'user',    
                window(':user.apiOne') => 'apiOne',    
                window(':user.apiTwo') => 'apiTwo',    
            ]
        );

    }


    fuction root() {

        //This is the home url
        self::load('some-file', fn() => compile());

    }


    /**
     * This is home/user/apiOne
     */
    fuction apiOne() {
        
        self::integrateAPI('ajax'); //response should be json format for shutters

        self::call([
        
            window('base:') => 'win:Routes\API\APIHandler';
            
        ]);

    }


    /**
     * This is home/user/apiTwo
     */
    fuction apiTwo() {

        Ajax::isAjax(':json'); //set Content-Type as application/json

        if(!Ajax::isAjax()){

            //run this is if request is NOT ajax request

            $response = ['message'=>'invalid request protocol', 'code' => 401];

            echo json_encode($response);

            return;

        } else {

            //run this is if request is ajax request

            if(routeExists('API\APIHandler')) {

                new API\APIHander();

            } else {

                $response = ['message'=>'api not found', 'code' => 404];

                echo json_encode($response);

                return;
                
            }

        }

    }

  }
                                    

If the home url is visited, then the method root() is called. When the home/user is visited, the user() method is called. When the home/user/apiOne is visited, the method apiOne() is called. Each url visited calls their corresponding methods on the Home class. This means that, according to the code above, the Home class is the urls entry point. While root() may load its file as a web page, the apiOne is managed by the integerateAPI() method along with the call() shutter. In apiTwo however, the entire logic is handled with the Ajax class and this is done through series of testing conditions. It can be easily noticed that it is much easier to implement integerateAPI() than testing with Ajax class. However, when we need to handle custom requests, then using the Ajax class is the best solution.

Previously defined examples have shown that the integerateAPI() method can be used to define a route type. This method can also be defined at top level by setting the static property $winAPI to any valid option accepted by integerateAPI() method. If the property is set, the window will use that value to set shutter responses. Example of this is shown below:

Example 2: Building API on routes
  class Home {

    static $winAPI  = 'json:ajax';

    function __construct(){

        self::call(
            [
                window(':') => 'root',    
                window(':user') => 'user',       
            ]
        );

    }

  }
                                    

In the code above, the call() method will respond as if the integrateAPI('json:ajax') was called on it.