Migration Guide: Upgrading to APIFromAnything 1.0.0
This guide will help you migrate your application from APIFromAnything 0.1.0 to 1.0.0. Version 1.0.0 includes significant improvements, including full async/await support, enhanced security features, and improved performance.
Overview of Changes
Major Changes
Full Async Support: All components now support async/await syntax
Improved Middleware System: Enhanced middleware architecture with better async support
Enhanced Security Features: Improved security middleware and decorators
Performance Optimizations: Better caching, connection pooling, and request coalescing
Comprehensive Documentation: Detailed documentation for all features
Improved Testing Framework: Better support for testing async functions
Breaking Changes
Middleware Interface: The middleware interface has changed to better support async functions
Security Decorators: Security decorators now handle async functions differently
Error Handling: Error handling has been improved and may require changes to your code
Configuration Options: Some configuration options have been renamed or changed
Testing Utilities: Testing utilities have been updated to support async functions
Step-by-Step Migration Guide
1. Update Your Dependencies
Update your requirements.txt file to use the latest version:
- apifrom==0.1.0
+ apifrom==1.0.0
Or install the latest version directly:
pip install --upgrade apifrom
2. Update Middleware Implementation
If you’ve created custom middleware, you’ll need to update it to use the new async interface:
from apifrom.middleware import Middleware
from apifrom.core.request import Request
from apifrom.core.response import Response
class CustomMiddleware(Middleware):
- def dispatch(self, request, call_next):
+ async def dispatch(self, request, call_next):
# Process request
print(f"Request: {request.method} {request.url.path}")
- response = call_next(request)
+ response = await call_next(request)
# Process response
print(f"Response: {response.status_code}")
return response
3. Update API Endpoints for Async Support
If you want to take advantage of async support, update your API endpoints:
from apifrom import API, api
app = API()
@api(route="/users", method="GET")
- def get_users():
+ async def get_users():
# Your code here
return {"users": [...]}
4. Update Database Operations
If you’re using database operations, update them to use async libraries:
- import sqlite3
+ import aiosqlite
@api(route="/users", method="GET")
- def get_users():
+ async def get_users():
- conn = sqlite3.connect("database.db")
- cursor = conn.cursor()
- cursor.execute("SELECT * FROM users")
- users = cursor.fetchall()
- conn.close()
+ async with aiosqlite.connect("database.db") as db:
+ async with db.execute("SELECT * FROM users") as cursor:
+ users = await cursor.fetchall()
return {"users": users}
5. Update HTTP Client Code
If you’re making HTTP requests, update them to use async HTTP clients:
- import requests
+ import aiohttp
@api(route="/proxy", method="GET")
- def proxy(url: str):
+ async def proxy(url: str):
- response = requests.get(url)
- data = response.json()
+ async with aiohttp.ClientSession() as session:
+ async with session.get(url) as response:
+ data = await response.json()
return {"data": data}
6. Update Security Decorators
If you’re using security decorators, update them to handle async functions:
from apifrom import API, api
from apifrom.security import jwt_required
app = API()
@api(route="/protected", method="GET")
@jwt_required(secret="your-secret-key")
- def protected_endpoint(request):
+ async def protected_endpoint(request):
user_id = request.state.jwt_payload.get("sub")
return {"message": f"Hello, user {user_id}!"}
7. Update Error Handling
If you’re using custom error handling, update it to handle async functions:
from apifrom import API, api
from apifrom.middleware import ErrorHandlingMiddleware
from apifrom.exceptions import BadRequestError
app = API()
app.add_middleware(
ErrorHandlingMiddleware(
debug=True,
- on_error=lambda request, exc, traceback: print(f"Error: {exc}")
+ on_error=async_error_handler
)
)
+ async def async_error_handler(request, exc, traceback):
+ print(f"Error: {exc}")
+ # You can now perform async operations here
+ return {"error": str(exc)}, 500
@api(route="/users/{user_id}", method="GET")
- def get_user(user_id: int):
+ async def get_user(user_id: int):
if user_id <= 0:
raise BadRequestError(message="User ID must be positive")
# Your code here
return {"id": user_id, "name": "John Doe"}
8. Update Testing Code
If you’re using the testing utilities, update them to support async functions:
- from apifrom.testing import TestClient, MiddlewareTester
+ from apifrom.testing import TestClient, AsyncMiddlewareTester
import pytest
- def test_api():
+ @pytest.mark.asyncio
+ async def test_api():
client = TestClient(app)
- response = client.get("/users")
+ response = await client.get("/users")
assert response.status_code == 200
assert "users" in response.json()
- def test_middleware():
+ @pytest.mark.asyncio
+ async def test_middleware():
- tester = MiddlewareTester(CustomMiddleware())
+ tester = AsyncMiddlewareTester(CustomMiddleware())
request = Request(method="GET", url="/test")
mock_response = Response(content={"message": "Test"}, status_code=200)
- response = tester.test_dispatch(request=request, mock_response=mock_response)
+ response = await tester.test_dispatch(request=request, mock_response=mock_response)
assert response.status_code == 200
9. Update Configuration Options
Some configuration options have been renamed or changed:
app = API(
title="My API",
version="1.0.0",
- enable_docs=True,
+ docs_url="/docs",
- enable_openapi=True,
+ openapi_url="/openapi.json",
- debug_mode=True,
+ debug=True,
)
10. Update Performance Optimization Code
If you’re using performance optimization features, update them to support async functions:
from apifrom import API, api
from apifrom.performance import cache, coalesce_requests, batch_process
app = API()
@api(route="/expensive-operation", method="GET")
@cache(ttl=60)
- def expensive_operation():
+ async def expensive_operation():
# Your code here
return {"result": "expensive computation"}
@api(route="/popular-data", method="GET")
@coalesce_requests(ttl=30, max_wait_time=0.05)
- def get_popular_data():
+ async def get_popular_data():
# Your code here
return {"data": "popular data"}
@api(route="/users", method="POST")
@batch_process(max_batch_size=100, max_wait_time=0.1)
- def create_users(user_data_batch):
+ async def create_users(user_data_batch):
# Your code here
return {"status": "success"}
Common Migration Issues and Solutions
Issue 1: Coroutine Was Never Awaited Warning
Problem: You see a warning like RuntimeWarning: coroutine 'function_name' was never awaited
Solution: Ensure you’re properly awaiting all async functions:
# Incorrect
@api(route="/users", method="GET")
async def get_users():
result = fetch_users() # Missing await
return result
# Correct
@api(route="/users", method="GET")
async def get_users():
result = await fetch_users() # Properly awaited
return result
Issue 2: Event Loop Already Running Error
Problem: You get an error like RuntimeError: This event loop is already running
Solution: Use asyncio.create_task() instead of asyncio.run() when inside an async function:
# Incorrect - inside an async function
async def outer_function():
# This will fail with "event loop already running"
result = asyncio.run(inner_function())
return result
# Correct - inside an async function
async def outer_function():
# Create a task instead
task = asyncio.create_task(inner_function())
result = await task
return result
Issue 3: Middleware Not Handling Async Functions
Problem: Your middleware is not properly handling async functions
Solution: Ensure your middleware is using await when calling the next middleware or endpoint:
# Incorrect middleware
class IncorrectMiddleware(Middleware):
def dispatch(self, request, call_next):
# Missing await
response = call_next(request)
return response
# Correct middleware
class CorrectMiddleware(Middleware):
async def dispatch(self, request, call_next):
# Properly awaited
response = await call_next(request)
return response
Issue 4: Testing Async Functions
Problem: You’re having trouble testing async functions
Solution: Use pytest.mark.asyncio to test async functions:
import pytest
# Mark the test as async
@pytest.mark.asyncio
async def test_async_function():
# Test async function
result = await async_function()
assert result == "expected result"
Issue 5: Mixing Sync and Async Code
Problem: You’re trying to call an async function from a synchronous function
Solution: Use asyncio.run() to call async functions from synchronous code:
import asyncio
# Synchronous function calling an async function
def sync_function():
# Use asyncio.run() to call async functions from sync code
result = asyncio.run(async_function())
return result
async def async_function():
await asyncio.sleep(1)
return "result"
Conclusion
Migrating to APIFromAnything 1.0.0 requires some changes to your code, but the benefits of full async support, improved security, and better performance are worth the effort. If you encounter any issues during migration, please refer to the troubleshooting guide or open an issue on the GitHub repository.
For more information about the new features in version 1.0.0, see the changelog.