A comprehensive REST API client library with advanced features
Project description
REST API Library
A comprehensive Python library for managing REST API clients with advanced features including authentication, caching, retry logic, request/response models, and environment variable support.
Table of Contents
- Features
- Installation
- Quick Start
- Configuration
- Authentication
- Caching
- Models
- Advanced Features
- API Reference
- Examples
- CLI Tools
- Contributing
Features
- ๐ Easy Configuration: JSON-based configuration with environment variable support
- ๐ Multiple Auth Types: Bearer, Basic, API Key, JWT, OAuth2, and Dynamic Token authentication
- ๐พ Smart Caching: In-memory and Redis caching with TTL support
- ๐ Retry Logic: Configurable retry with exponential backoff and jitter
- ๐ Request/Response Models: Type-safe models with validation
- ๐ Environment Variables: Secure configuration with .env file support
- ๐ Comprehensive Logging: Detailed request/response logging with sensitive data masking
- ๐ฏ Multiple API Versions: Support for different API versions
- โก High Performance: Optimized for speed and reliability
- ๐ ๏ธ CLI Tools: Command-line tools for validation and template generation
Installation
Basic Installation
pip install restapi-library
With Redis Support
pip install restapi-library[redis]
Development Installation
pip install restapi-library[dev]
Quick Start
1. Create Configuration File
Create a config.json file:
{
"api-payment": {
"base_url": "${API_BASE_URL:https://api.payment.com}",
"default_version": "v1",
"auth": {
"type": "bearer",
"token": "${API_TOKEN}"
},
"endpoints": {
"v1": {
"get_user": {
"path": "/users/{user_id}",
"method": "GET",
"params": {
"user_id": {
"type": "int",
"required": true
}
},
"auth_required": true
}
}
}
}
}
2. Create Environment File
Create a .env file:
API_BASE_URL=https://api.payment.com
API_TOKEN=your_secret_token_here
3. Use the Library
from restapi_library import RestAPILibrary
# Initialize the library
api = RestAPILibrary(config_path="config.json")
# Make API calls
response = api.api_payment.get_user(params={'user_id': 123})
print(response)
Configuration
Basic Structure
{
"api-name": {
"base_url": "https://api.example.com",
"default_version": "v1",
"timeout": 30,
"raise_on_error": true,
"auth": { ... },
"cache": { ... },
"logging": { ... },
"retry": { ... },
"endpoints": { ... }
}
}
Environment Variables
Use ${VARIABLE_NAME} syntax for environment variables:
{
"api-name": {
"base_url": "${API_BASE_URL:https://api.default.com}",
"auth": {
"type": "bearer",
"token": "${API_TOKEN}"
}
}
}
Format: ${VARIABLE_NAME:default_value}
Global Settings
| Setting | Type | Default | Description |
|---|---|---|---|
base_url |
string | Required | Base URL for the API |
default_version |
string | "v1" | Default API version |
timeout |
int | 30 | Request timeout in seconds |
raise_on_error |
bool | true | Raise exception on HTTP errors |
Retry Configuration
{
"retry": {
"attempts": 3,
"delay": 1.0,
"backoff_factor": 2.0,
"jitter": true
}
}
Logging Configuration
{
"logging": {
"enabled": true,
"level": "INFO",
"log_requests": true,
"log_responses": true,
"log_sensitive_data": false
}
}
Authentication
Bearer Token
{
"auth": {
"type": "bearer",
"token": "${API_TOKEN}"
}
}
Basic Authentication
{
"auth": {
"type": "basic",
"username": "${API_USERNAME}",
"password": "${API_PASSWORD}"
}
}
API Key Authentication
{
"auth": {
"type": "api_key",
"api_key": "${API_KEY}",
"key_name": "X-API-Key",
"location": "header"
}
}
Dynamic Token Authentication
For APIs that require login to get access tokens:
{
"auth": {
"type": "dynamic_token",
"login_endpoint": {
"path": "/auth/login",
"method": "POST",
"body": {
"username": "${API_USERNAME}",
"password": "${API_PASSWORD}"
},
"token_field": "access_token",
"refresh_token_field": "refresh_token",
"expires_in_field": "expires_in"
},
"token_placement": {
"type": "header",
"header_name": "Authorization",
"prefix": "Bearer"
},
"refresh_endpoint": {
"path": "/auth/refresh",
"method": "POST",
"body_field": "refresh_token"
},
"cache": {
"type": "redis",
"ttl": 3600
}
}
}
Caching
In-Memory Cache
{
"cache": {
"type": "memory",
"ttl": 3600
}
}
Redis Cache
First, set Redis configuration:
RestAPILibrary.set_redis_config({
"host": "localhost",
"port": 6379,
"db": 0,
"password": "your_password"
})
Then use in configuration:
{
"cache": {
"type": "redis",
"ttl": 3600
}
}
Endpoint-Level Caching
{
"endpoints": {
"v1": {
"get_data": {
"path": "/data",
"method": "GET",
"cache": {
"enabled": true,
"ttl": 300
}
}
}
}
}
Models
Defining Models
from restapi_library import BaseModel
from dataclasses import dataclass
from typing import Optional
@dataclass
class UserModel(BaseModel):
name: str
email: str
age: Optional[int] = None
def validate(self) -> bool:
return "@" in self.email and len(self.name) > 0
# Register the model
RestAPILibrary.register_model('UserModel', UserModel)
Using Models in Configuration
{
"endpoints": {
"v1": {
"create_user": {
"path": "/users",
"method": "POST",
"request_model": "UserModel",
"response_model": "UserModel",
"body_required": true
}
}
}
}
Using Models in Code
# Create model instance
user = UserModel(name="John Doe", email="john@example.com", age=30)
# Make API call with model
response = api.my_api.create_user(body=user)
# Response is automatically parsed to model if response_model is configured
print(response.data.name) # Access model properties
Advanced Features
Multiple API Versions
{
"endpoints": {
"v1": {
"get_user": {
"path": "/users/{user_id}",
"method": "GET"
}
},
"v2": {
"get_user": {
"path": "/v2/users/{user_id}",
"method": "GET",
"response_model": "UserV2Model"
}
}
}
}
Usage:
# Use default version (v1)
user_v1 = api.my_api.get_user(params={'user_id': 123})
# Use specific version
user_v2 = api.my_api.get_user_v2(params={'user_id': 123})
Parameter Validation
{
"endpoints": {
"v1": {
"get_user": {
"path": "/users/{user_id}",
"method": "GET",
"params": {
"user_id": {
"type": "int",
"required": true
},
"include_profile": {
"type": "bool",
"required": false
}
}
}
}
}
}
Custom Headers and Timeouts
# Custom headers
response = api.my_api.get_user(
params={'user_id': 123},
headers={'X-Custom-Header': 'value'}
)
# Custom timeout
response = api.my_api.get_user(
params={'user_id': 123},
timeout=60
)
Error Handling
from restapi_library import APIError, AuthenticationError
try:
response = api.my_api.get_user(params={'user_id': 123})
except AuthenticationError as e:
print(f"Authentication failed: {e}")
except APIError as e:
print(f"API error {e.status_code}: {e}")
print(f"Response data: {e.response_data}")
except Exception as e:
print(f"Unexpected error: {e}")
API Reference
RestAPILibrary Class
Constructor
RestAPILibrary(
config_path: Optional[str] = None,
config_dict: Optional[Dict] = None,
env_file: Optional[str] = '.env',
logger: Optional[APILogger] = None
)
Class Methods
# Set global Redis configuration
RestAPILibrary.set_redis_config(redis_config: Dict[str, Any])
# Register model for request/response handling
RestAPILibrary.register_model(name: str, model_class: type)
Instance Methods
# Get API client by name
get_client(api_name: str) -> APIClient
# List all available API names
list_apis() -> List[str]
# Reload configuration
reload_config(config_path: Optional[str] = None, config_dict: Optional[Dict] = None)
BaseModel Class
Methods
# Convert model to dictionary
to_dict() -> Dict[str, Any]
# Convert model to JSON string
to_json() -> str
# Create model from dictionary
@classmethod
from_dict(cls, data: Dict[str, Any]) -> BaseModel
# Create model from JSON string
@classmethod
from_json(cls, json_str: str) -> BaseModel
# Validate model data (override in subclasses)
validate() -> bool
APIResponse Class
Properties
# Parsed data (as model or raw data)
data: Union[BaseModel, Dict, Any]
# HTTP status code
status_code: int
# Response headers
headers: Dict[str, str]
# Check if response is successful (2xx status)
is_success: bool
# Raw response data
raw_data: Any
Examples
Complete Example with All Features
from restapi_library import RestAPILibrary, BaseModel
from dataclasses import dataclass
from typing import Optional, List
# Define models
@dataclass
class UserModel(BaseModel):
id: Optional[int] = None
name: str = ""
email: str = ""
age: Optional[int] = None
def validate(self) -> bool:
return "@" in self.email and len(self.name) > 0
@dataclass
class ProductModel(BaseModel):
id: Optional[int] = None
name: str = ""
price: float = 0.0
category: str = ""
def validate(self) -> bool:
return self.price > 0 and len(self.name) > 0
def main():
# Set up Redis (optional)
RestAPILibrary.set_redis_config({
"host": "localhost",
"port": 6379,
"db": 0
})
# Register models
RestAPILibrary.register_model('UserModel', UserModel)
RestAPILibrary.register_model('ProductModel', ProductModel)
# Initialize library
api = RestAPILibrary(config_path="config.json")
try:
# Example 1: Simple GET request
print("=== GET User ===")
user_response = api.ecommerce_api.get_user(params={'user_id': 123})
print(f"User: {user_response.data.name} ({user_response.data.email})")
# Example 2: POST request with model
print("\n=== CREATE User ===")
new_user = UserModel(
name="Jane Smith",
email="jane@example.com",
age=25
)
create_response = api.ecommerce_api.create_user(body=new_user)
print(f"Created user ID: {create_response.data.id}")
# Example 3: GET with query parameters
print("\n=== GET Products ===")
products_response = api.ecommerce_api.get_products(
params={
'category': 'electronics',
'min_price': 100,
'max_price': 1000,
'page': 1,
'limit': 10
}
)
print(f"Found {len(products_response.data)} products")
# Example 4: Different API version
print("\n=== GET User (v2) ===")
user_v2_response = api.ecommerce_api.get_user_v2(params={'user_id': 123})
print(f"User v2: {user_v2_response}")
# Example 5: Custom headers and timeout
print("\n=== Custom Request ===")
custom_response = api.ecommerce_api.get_user(
params={'user_id': 123},
headers={
'X-Client-Version': '1.0',
'X-Request-ID': 'req-12345'
},
timeout=60
)
print(f"Custom request successful: {custom_response.is_success}")
# Example 6: Error handling
print("\n=== Error Handling ===")
try:
error_response = api.ecommerce_api.get_user(params={'user_id': -1})
except Exception as e:
print(f"Expected error: {type(e).__name__}: {e}")
# Example 7: List all available APIs
print(f"\n=== Available APIs ===")
apis = api.list_apis()
print(f"Available APIs: {apis}")
except Exception as e:
print(f"Error: {e}")
if __name__ == "__main__":
main()
Configuration File for the Example
{
"ecommerce-api": {
"base_url": "${ECOMMERCE_API_URL:https://api.ecommerce.com}",
"default_version": "v1",
"timeout": 30,
"auth": {
"type": "dynamic_token",
"login_endpoint": {
"path": "/auth/login",
"method": "POST",
"body": {
"username": "${ECOMMERCE_USERNAME}",
"password": "${ECOMMERCE_PASSWORD}"
},
"token_field": "access_token",
"refresh_token_field": "refresh_token",
"expires_in_field": "expires_in"
},
"token_placement": {
"type": "header",
"header_name": "Authorization",
"prefix": "Bearer"
},
"refresh_endpoint": {
"path": "/auth/refresh",
"method": "POST",
"body_field": "refresh_token"
},
"cache": {
"type": "redis",
"ttl": 3600
}
},
"cache": {
"type": "redis",
"ttl": 300
},
"logging": {
"enabled": true,
"level": "INFO",
"log_requests": true,
"log_responses": true,
"log_sensitive_data": false
},
"retry": {
"attempts": 3,
"delay": 1.0,
"backoff_factor": 2.0,
"jitter": true
},
"endpoints": {
"v1": {
"get_user": {
"path": "/users/{user_id}",
"method": "GET",
"params": {
"user_id": {
"type": "int",
"required": true
}
},
"response_model": "UserModel",
"auth_required": true,
"cache": {
"enabled": true,
"ttl": 300
},
"retry": {
"attempts": 3,
"delay": 1.0
}
},
"create_user": {
"path": "/users",
"method": "POST",
"request_model": "UserModel",
"response_model": "UserModel",
"body_required": true,
"auth_required": true,
"raise_on_error": true
},
"get_products": {
"path": "/products",
"method": "GET",
"params": {
"category": {
"type": "str",
"required": false
},
"min_price": {
"type": "float",
"required": false
},
"max_price": {
"type": "float",
"required": false
},
"page": {
"type": "int",
"required": false
},
"limit": {
"type": "int",
"required": false
}
},
"auth_required": true,
"cache": {
"enabled": true,
"ttl": 600
}
}
},
"v2": {
"get_user": {
"path": "/v2/users/{user_id}",
"method": "GET",
"params": {
"user_id": {
"type": "int",
"required": true
}
},
"auth_required": true
}
}
}
}
}
Environment File (.env)
# API Configuration
ECOMMERCE_API_URL=https://api.ecommerce.com
ECOMMERCE_USERNAME=your_username
ECOMMERCE_PASSWORD=your_secure_password
# Redis Configuration (if using Redis cache)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=your_redis_password
# Other API configurations
PAYMENT_API_URL=https://api.payment.com
PAYMENT_API_KEY=your_payment_api_key
CLI Tools
The library includes command-line tools for configuration management.
Validate Configuration
restapi-cli validate config.json
Output:
โ Configuration file 'config.json' is valid
API: ecommerce-api
Base URL: https://api.ecommerce.com
Default Version: v1
Auth Type: dynamic_token
Endpoints: 3
Generate Configuration Template
restapi-cli template -o my_config.json
This generates a template configuration file with all available options.
Error Handling
Exception Types
| Exception | Description |
|---|---|
RestAPIException |
Base exception for all library errors |
ConfigurationError |
Configuration file or setup errors |
AuthenticationError |
Authentication-related errors |
ValidationError |
Request/response validation errors |
APIError |
HTTP response errors (4xx, 5xx) |
RetryExhaustedError |
All retry attempts failed |
CacheError |
Cache-related errors |
TokenExpiredError |
Authentication token expired |
Error Handling Best Practices
from restapi_library import (
RestAPILibrary, APIError, AuthenticationError,
ValidationError, ConfigurationError
)
try:
api = RestAPILibrary(config_path="config.json")
response = api.my_api.get_data(params={'id': 123})
except ConfigurationError as e:
print(f"Configuration error: {e}")
# Handle configuration issues
except AuthenticationError as e:
print(f"Authentication failed: {e}")
# Handle auth issues, maybe refresh token
except ValidationError as e:
print(f"Validation error: {e}")
# Handle parameter or model validation errors
except APIError as e:
print(f"API error {e.status_code}: {e}")
if e.status_code == 429:
# Handle rate limiting
pass
elif e.status_code >= 500:
# Handle server errors
pass
except Exception as e:
print(f"Unexpected error: {e}")
# Handle other errors
Performance Tips
1. Use Caching Wisely
{
"cache": {
"type": "redis",
"ttl": 300
}
}
Cache GET requests that don't change frequently.
2. Configure Timeouts
{
"timeout": 30,
"endpoints": {
"v1": {
"slow_endpoint": {
"timeout": 120
}
}
}
}
Set appropriate timeouts for different endpoints.
3. Use Connection Pooling
The library uses requests.Session internally for connection pooling.
4. Optimize Retry Settings
{
"retry": {
"attempts": 3,
"delay": 1.0,
"backoff_factor": 2.0,
"jitter": true
}
}
Don't over-retry for endpoints that are unlikely to succeed on retry.
Security Best Practices
1. Use Environment Variables
Never hardcode sensitive information in configuration files:
{
"auth": {
"type": "bearer",
"token": "${API_TOKEN}"
}
}
2. Enable Sensitive Data Masking
{
"logging": {
"log_sensitive_data": false
}
}
3. Use HTTPS
Always use HTTPS URLs in production:
{
"base_url": "https://api.example.com"
}
4. Secure Redis Connection
If using Redis caching:
RestAPILibrary.set_redis_config({
"host": "your-redis-host",
"port": 6379,
"password": "your-secure-password",
"ssl": True,
"ssl_cert_reqs": "required"
})
Troubleshooting
Common Issues
1. Configuration File Not Found
ConfigurationError: Configuration file not found: config.json
Solution: Ensure the configuration file path is correct and the file exists.
2. Environment Variable Not Set
KeyError: 'API_TOKEN'
Solution: Check your .env file and ensure all required environment variables are set.
3. Redis Connection Failed
CacheError: Failed to connect to Redis: [Errno 111] Connection refused
Solution:
- Ensure Redis server is running
- Check Redis connection parameters
- Verify network connectivity
4. Authentication Failed
AuthenticationError: Login failed with status 401
Solution:
- Verify authentication credentials
- Check if API endpoints are correct
- Ensure token format is correct
5. Model Validation Failed
ValidationError: Body validation failed for model 'UserModel'
Solution:
- Check model validation logic
- Ensure required fields are provided
- Verify data types match model definition
Debug Mode
Enable detailed logging for debugging:
{
"logging": {
"enabled": true,
"level": "DEBUG",
"log_requests": true,
"log_responses": true,
"log_sensitive_data": true
}
}
Note: Only enable log_sensitive_data in development environments.
Contributing
Development Setup
- Clone the repository:
git clone https://github.com/yourusername/restapi-library.git
cd restapi-library
- Create virtual environment:
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
- Install in development mode:
pip install -e .[dev]
- Run tests:
pytest
- Run linting:
flake8 restapi_library
black restapi_library
mypy restapi_library
Project Structure
restapi_library/
โโโ __init__.py # Main library interface
โโโ core/ # Core functionality
โ โโโ __init__.py
โ โโโ client.py # Main API client
โ โโโ config.py # Configuration parser
โ โโโ endpoint.py # Endpoint class
โ โโโ exceptions.py # Custom exceptions
โ โโโ types.py # Type definitions
โ โโโ defaults.py # Default configurations
โโโ auth/ # Authentication handlers
โ โโโ __init__.py
โ โโโ base.py # Base auth handler
โ โโโ handlers.py # Built-in auth handlers
โ โโโ dynamic_token.py # Dynamic token auth
โ โโโ factory.py # Auth factory
โโโ models/ # Request/response models
โ โโโ __init__.py
โ โโโ base.py # Base model class
โ โโโ registry.py # Model registry
โ โโโ response.py # Response wrapper
โโโ cache/ # Caching implementations
โ โโโ __init__.py
โ โโโ base.py # Base cache interface
โ โโโ memory.py # In-memory cache
โ โโโ redis_cache.py # Redis cache
โ โโโ factory.py # Cache factory
โโโ utils/ # Utility functions
โ โโโ __init__.py
โ โโโ retry.py # Retry logic
โ โโโ logging.py # Logging utilities
โ โโโ env_parser.py # Environment parser
โ โโโ validation.py # Validation utilities
โ โโโ helpers.py # Helper functions
โ โโโ decorators.py # Custom decorators
โโโ cli.py # Command-line interface
โโโ examples/ # Example usage
โโโ config.json
โโโ .env
โโโ usage.py
License
This project is licensed under the MIT License - see the LICENSE file for details.
Changelog
Version 1.0.0
- Initial release
- Basic REST API client functionality
- Multiple authentication methods
- Caching support (memory and Redis)
- Request/response models
- Environment variable support
- Retry logic with exponential backoff
- Comprehensive logging
- CLI tools
Support
For support, please:
- Check the documentation
- Look at examples
- Check troubleshooting
- Open an issue on GitHub
Roadmap
- GraphQL support
- Async/await support
- Webhook handling
- OpenAPI/Swagger integration
- Rate limiting
- Circuit breaker pattern
- Metrics and monitoring
- Plugin system
- Mock server for testing
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file restapi_library-0.0.2.tar.gz.
File metadata
- Download URL: restapi_library-0.0.2.tar.gz
- Upload date:
- Size: 33.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52b4cd7769dad671ec6bb6090120938fd89966aae807d3421cfb0a156f929ddc
|
|
| MD5 |
5ff9ad1d25223ae70c2e2ab49ad3df92
|
|
| BLAKE2b-256 |
74b91a1fa9d2c65ce42535fcd120a3a6c78540a21c378174c42763de035f0a3b
|
File details
Details for the file restapi_library-0.0.2-py3-none-any.whl.
File metadata
- Download URL: restapi_library-0.0.2-py3-none-any.whl
- Upload date:
- Size: 35.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cfbf675fdb18352e70c0eb68f83a8a10cd35fa2195a43e70c10ce1238a8c6534
|
|
| MD5 |
a5b1e882aa1cc4e03a449161cf36a02e
|
|
| BLAKE2b-256 |
690dd36a6ca6bb9d2f1a6fca482d37dca6a9322706fbe88a6f2f19c81769248f
|