Lesson 5: API Basics

Overview

Apps require data to work properly. You can fetch the data from a Database, file or Server. In this last scenario, you will need to fetch or send data to an API. To work with APIs you will need to learn how to handle asynchronous scenarios. Because the server will take some time to process your request and then respond it.

Coding Problem

Applications need to work with an API (usually RESTful that comes from REST, REpresentational State Transfer) in order to interact with the data needed to run the application.

Solution

Building API calls that perform the interactions with the database needed to run the application. These reside in the controllers. These calls allow applications to get, create, edit and delete data in the database, amongst other functions.

What Calls Are

The server API can work with any network protocols even a new private protocol if you or your company wants to create one (not recommended). Normally the API works with Hypertext Transfer Protocol (common known as only HTTP).

The HTTP has different rules based on the request method used:

  • GET

    • Request data from the server. Use this method to retrieve data.

  • HEAD

    • It works as the GET method but without response body. It is useful to know if the data has been updated.

  • POST

    • Send data to the server

  • PUT

    • Replace the data on the server with the request payload.

  • DELETE

    • Delete data from the server.

  • CONNECT

    • Establishes a connection with the server.

  • OPTIONS

    • Get the communication options from the server.

  • TRACE

    • Perform a loopback test on the path.

  • PATCH

    • Send partial modifications data to the server.

Structuring calls

Web

As a reference you can check the Example Project code.

  • Structuring call

    • You can use Fetch API to call fetch with the URL you want. By default the Fetch API uses the GET method, so a very simple call would be like this:

  • fetch(url) // Call the fetch function passing the url of the API as a parameter
    .then(function() {
        // Your code for handling the data you get from the API
    })
    .catch(function() {
        // This is where you run code if the server returns any errors
    });{ ... }

    Handle the response

    • Implement the code responsible to handle the objects (data, response, error) that you receive from Fetch API

    function createNode(element) {
          return document.createElement(element);
      }
    function append(parent, el) {
        return parent.appendChild(el);
      }
    const ul = document.getElementById('authors');
      const url = '
    https://randomuser.me/api/?results=25
    ';
      fetch(url)
      .then((resp) => resp.json()) //Transform the data into json
      .then(function(data) {
        let authors = data.results; //Get the results
        return authors.map(function(author) { //Create the elements we need
          let li = createNode('li'),
              img = createNode('img'),
              span = createNode('span');
          img.src = author.picture.medium;
          span.innerHTML = ${author.name.first} ${author.name.last};
          append(li, img);
          append(li, span);
          append(ul, li); //Append all our elements
        })
      })
      .catch(function(error) {
        console.log(error); //Show on console log the error
      });

iOS

As a reference you can check the Example Project code.

  • Structuring call

    • Create a simple and exclusive method to call the backend. On our example the function fetchListInfo

    public func fetchListInfo(onSuccess: @escaping () -> Void, onFail: @escaping (String) -> Void) { ... }

The fetchListInfo method receives as parameters two methods, onSuccess and onFail respectively. For onSuccess the method will not have any parameter and there is no return. The onSuccess method will be called on success scenario. For onFail method it will receive a String parameter and there is no return. The onFail method will be called on fail scenario.

  • Call backend

    • Implement the fetchListInfo method body code. On our example the code is:

    • func fetchListInfo(onSuccess: @escaping () -> Void, onFail: @escaping (String) -> Void) {
          guard let urlRequest = URL(string: "https://randomuser.me/api/?results=25") else {
              onFail("Not possible to create the URL object")
              return
          }
      
          let config = URLSessionConfiguration.default
          let session = URLSession(configuration: config)
      
          let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
              print("it's alive!")
          })
      
          task.resume()
      }

      We have to create a DataTask object using the Session object and also we have to implement the completitionHandler, because only on this way we can handle properly the response from backend. On our example the fetch body method will submit a HTTP Request to https://randomuser.me/api/?results=25 and the code under completionHandler variable will handle the response.

  • Handle the response

    • Implement the code responsible to handle the objects (data, response, error) that you receive from completitionHandler

  • func fetchListInfo(onSuccess: @escaping () -> Void, onFail: @escaping (String) -> Void) {
        guard let urlRequest = URL(string: "https://randomuser.me/api/?results=25") else {
            onFail("Not possible to create the URL object")
            return
        }
    
        let config = URLSessionConfiguration.default
        let session = URLSession(configuration: config)
    
        let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
            if let error = error {
                onFail(error.localizedDescription)
            } else {
                guard let httpResponse = response as? HTTPURLResponse,
                    httpResponse.statusCode == 200
                    else {
                        onFail("Error on fetch.")
                        return
                }
    
                if let data = data {
                    self.list = try! self.convertToUsers(withData: data)
                    onSuccess()
                } else {
                    onFail("Data is null")
                }
            }
        })
    
        task.resume()
    }

    Based on the response variables (data, response, error) we will decide which method to call onSuccess or onFail to notify the interface. On our example we have multiple scenarios where onFail will be called. However, for onSuccess call is only one scenario, when all necessary variables are valid.

Android

As a reference you can check the Example Project code.

  • Structuring call

    • Create a simple and exclusive method to call the backend. On our example the function fetchPersonRequest

    • private JsonObjectRequest fetchPersonRequest() { ... }

      The fetchPersonRequest method does not receive parameters. It uses an interface and delegate design pattern to trigger the interface on success and fail scenarios.

  • Call backend

    • Implement the fetchPersonRequest method body code. On our example the code is:

    • private JsonObjectRequest fetchPersonRequest() {
              JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET,
                      "https://randomuser.me/api/?results=25",
      
                      new Response.Listener<JSONObject>() {
                          @Override
                          public void onResponse(JSONObject response) {
                              Log.i("Controller", "It's alive!");
                          }
                      },
      
                      new Response.ErrorListener() {
                          @Override
                          public void onErrorResponse(VolleyError error) {
                              // On error response, implement callback for it too
                              Log.e("Controller", "Server ERROR: " + error.getMessage());
                          }
                      });
      
              return jsonObjectRequest;
          }

      We have to create a JsonObjectRequest object and also we have to override the onResponse and onErrorResponse methods to be able to act on both situation happens. On our example the fetch body method will submit a HTTP Request to https://randomuser.me/api/?results=25 and the code under onResponse will handle the response and the code under onErrorResponse will handle the error scenario.

  • Handle the responses

    • Implement the code responsible to handle both scenarios, onResponse and onErrorResponse.

  • private JsonObjectRequest fetchPersonRequest() {
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET,
            "https://randomuser.me/api/?results=25",
    
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    // To avoid create variable inside of loops
                    ArrayList<Person> returnList = new ArrayList<Person>();
                    JSONArray jsonArray = null;
                    JSONObject jsonObject = null;
    
                    try {
                        jsonArray = response.getJSONArray("results");
    
                        for (int i = 0; i <jsonArray.length(); i++) {
                            try {
                                jsonObject = jsonArray.getJSONObject(i);
    
                                returnList.add(convertDataToCustomObject(jsonObject));
                            } catch (JSONException e) {
                                e.printStackTrace();
                                iPersonApi.fetchFailure("JSON read failure");
                                break;
                            } catch (Exception e) {
                                e.printStackTrace();
                                iPersonApi.fetchFailure("Unknown failure");
                                break;
                            }
                        }
    
                        iPersonApi.fetchSuccess(returnList);
                    } catch (JSONException e) {
                        e.printStackTrace();
    
                        iPersonApi.fetchFailure("JSON read failure");
                    } catch (Exception e) {
                        e.printStackTrace();
    
                        iPersonApi.fetchFailure("Unknown failure");
                    }
                }
    
            },
    
            new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    // On error response, implement callback for it too
                    Log.e("API error", "Server ERROR: " + error.getMessage());
                    iPersonApi.fetchFailure(error.getMessage());
                }
            });
    
        return jsonObjectRequest;
    }

    Based on the response methods we will decide which delegate method to call to notify the interface. On our example we have multiple scenarios where delegate fail method will be called. However, for success delegate method call is only one scenario, when all necessary variables are valid.

Headers / Responses

HTTP protocols have the header section that allows the communication between client and server to provide more information with the request or response. The request header consist of a dictionary of keys and values where the key is separated by the symbol `:` from its value.

Using the headers, it is possible to know if the server accepts a JSON payload or not, or check if there is an update on the data.

The response is an HTTP message as the Request, but in this case, it is what the Server sends to the client after receiving an HTTP request. Then the HTTP Response also has the Headers and the Body (when necessary).

Network / Status Codes

When the client needs data from the server it has to send the HTTP Request and wait for the HTTP Response. However, sometimes the response is not what the client is expecting. The way to guarantee that what is inside of the HTTP Response is what the client expects is to check the Status Code from the HTTP Response object.

There are several status code, but the most common are 400, 404, 500 and 200. However, what do these status codes mean?

Anything on the 2xx range is a Successful scenario:

  • 200 Ok

    • A successful processing the of the request.

  • 202 Accepted

    • The request has been accepted for processing, but the processing has not been completed.

  • 204 No Content

    • The server successfully processed the request but is not returning any content.

  • 206 Partial Content

    • The server is delivering only part of the resource (byte serving) due to a range header sent by the client

Anything on the 4xx range is Client Error scenario:

  • 400 Bad Request

    • The server cannot or will not process the request due to an apparent client error

  • 401 Unauthorized

    • Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or has not yet been provided.

  • 404 Not Found

    • The requested resource could not be found but may be available in the future.

  • 408 Request Timeout

    • The server is delivering only part of the resource (byte serving) due to a range header sent by the client

  • 423 Too Many Requests

    • The user has sent too many requests in a given amount of time.

Anything on 5xx range is Server Error scenario:

  • 500 Internal Server Error

    • A generic error message, given when an unexpected condition was encountered and no more specific message is suitable

  • 501 Not Implemented

    • The server either does not recognize the request method, or it lacks the ability to fulfil the request.

  • 502 Bad Gateway

    • The server was acting as a gateway or proxy and received an invalid response from the upstream server

  • 503 Service Unavailable

    • The server is currently unavailable (because it is overloaded or down for maintenance)

  • 508 Loop Detected

    • The server detected an infinite loop while processing the request

A callback function is a function provided to another function as a parameter, which is then invoked inside the outer function to complete some kind of routine or action.

When you start to deal with Server or other systems online you can not expect them to work in a synchronous way with your application. All of this interaction will happen in an asynchronous way. When the client sends the request to the server the client should hold the execution of the application or web page until the server sends the response.

To handle this scenario properly, we have to use callbacks to handle asynchronous scenarios. In this way, when the server sends the response the client will handle the response when it arrives via callback and, using the same concept, we can notify the user interface.

Last updated