Hone logo
Hone
Problems

Building a Robust REST API Client in Python

This challenge focuses on creating a reusable and reliable REST API client in Python. A well-designed client simplifies interacting with external APIs, handling common tasks like request construction, error handling, and response parsing. This is a fundamental skill for any Python developer working with web services.

Problem Description

You are tasked with building a Python class, APIClient, that acts as a client for interacting with REST APIs. The client should handle making GET, POST, PUT, and DELETE requests to specified endpoints. It should also manage authentication (using a provided API key), handle potential errors gracefully, and return parsed JSON responses.

Key Requirements:

  • Base URL: The client should be initialized with a base URL for the API.
  • Authentication: The client should accept an API key during initialization and include it in the Authorization header for each request.
  • HTTP Methods: Implement methods for GET, POST, PUT, and DELETE requests. Each method should accept an endpoint (relative to the base URL) and optional data (for POST/PUT).
  • Error Handling: The client should handle HTTP errors (status codes 4xx and 5xx) by raising appropriate exceptions with informative error messages.
  • JSON Parsing: The client should automatically parse JSON responses into Python dictionaries.
  • Headers: The client should set the Content-Type header to application/json for POST and PUT requests.

Expected Behavior:

  • Successful requests should return a Python dictionary representing the parsed JSON response.
  • Failed requests (HTTP errors) should raise an APIError exception.
  • The client should handle network errors gracefully.

Edge Cases to Consider:

  • Invalid JSON responses.
  • Missing API key.
  • Malformed endpoint URLs.
  • Network connectivity issues.
  • Empty responses.

Examples

Example 1:

Input:
api_client = APIClient(base_url="https://api.example.com", api_key="YOUR_API_KEY")
response = api_client.get(endpoint="/users/123")

Output:
{'id': 123, 'name': 'John Doe', 'email': 'john.doe@example.com'}

Explanation:
A GET request is made to "https://api.example.com/users/123" with the API key in the Authorization header. The response is assumed to be valid JSON and is parsed into a Python dictionary.

Example 2:

Input:
api_client = APIClient(base_url="https://api.example.com", api_key="YOUR_API_KEY")
response = api_client.post(endpoint="/users", data={'name': 'Jane Doe', 'email': 'jane.doe@example.com'})

Output:
{'id': 456, 'name': 'Jane Doe', 'email': 'jane.doe@example.com'}

Explanation:
A POST request is made to "https://api.example.com/users" with the provided data and the API key. The `Content-Type` header is set to `application/json`. The response is parsed as JSON.

Example 3: (Error Handling)

Input:
api_client = APIClient(base_url="https://api.example.com", api_key="YOUR_API_KEY")
try:
    response = api_client.get(endpoint="/nonexistent_resource")
except APIError as e:
    print(f"Error: {e}")

Output:
Error: HTTP Error 404: Not Found - Not Found

Explanation:
A GET request to a non-existent resource results in a 404 error. The client catches this error and raises an APIError exception with a descriptive message.

Constraints

  • The API client should be implemented as a class.
  • Use the requests library for making HTTP requests. You will need to install it: pip install requests.
  • The APIError exception should be a custom exception class inheriting from Exception.
  • The base URL should be a valid URL string.
  • The API key should be a non-empty string.
  • The endpoint should be a string.
  • Data for POST/PUT requests should be a Python dictionary.
  • The client should handle timeouts gracefully (e.g., a timeout of 5 seconds).

Notes

  • Consider using try...except blocks to handle potential errors during request execution and JSON parsing.
  • Think about how to structure your code for reusability and maintainability.
  • The requests library provides many useful features for making HTTP requests. Refer to its documentation for more information.
  • Focus on creating a robust and well-documented client that can handle various scenarios.
  • Remember to include appropriate docstrings for your class and methods.
  • Consider adding logging for debugging purposes.
Loading editor...
python