API

What is an API?

  • set of data (often in JSON)

  • access different endpoints of API to get certain data (we usually don't want all the data)

Request Handler

When building an application that queries from an API a lot, we need to expect errors in handling requests to the API. We can build a handler class to handle all the exceptions without filling our code with try-excepts.

General rule of thumb: When we find we have many repetitive try-excepts, build a handler class.

"""Class with methods that handle requests to API"""
import json
import time
from typing import Union

import requests

from handlers.handler import Handler
from logger import logger

base_url = "http://some-api.com"

# Handler class is a parent class that handles parsing the credentials
class RequestHandler(Handler):
    """Class that handles requests"""

    def __init__(self, *args, **kwargs) -> None:
        super(RequestHandler, self).__init__(*args, **kwargs)

        api_credentials = self.get_credentials() # this method comes from the parent Handler class
        self.default_params = {"some_param": "some_value"}

    def get_request(self, url_path: str, params=None) -> Union[None, requests.Response]:
        """Sends GET request to API"""
        if params:
            self.default_params.update(params)

        request_url = base_url + url_path

        try:
            response = requests.get(
                request_url,
                headers={"Content-Type": "application/json"},
                params=self.default_params,
                auth=(credentials["user"], credentials["password"]),
            )
        except requests.exceptions.RequestException as err:
            logger.error("Unable to fetch %s\n%s", err, request_url)
            return None

        if hasattr(response, "status_code"):
            status_code = response.status_code
        else:
            logger.error("Response has no status_code attribute")
            return None

        if status_code == 429:
            time.sleep(60)
            response = self.get_request(url_path, params=params, api=api)

        if status_code != 200:
            logger.error(
                "Unable to fetch - return code %s\n%s", status_code, request_url
            )
            return None

        return response

    def get_response_json(
        self, response: requests.Response, requested_info: str
    ) -> Union[None, dict]:
        """Gets JSON from response"""
        if not response:
            return None

        try:
            response_json = response.json()
        except json.decoder.JSONDecodeError:
            logger.error("Could not decode JSON for %s", requested_info)
            response_json = None

        return response_json

Example on how to use request class:

response = request_handler.get_request("some_path")
response_json = request_handler.get_response_json(response, "some_log")

Last updated