Getting Started with APIFromAnything

This guide will help you get started with APIFromAnything, a powerful Python library for creating APIs from any Python function. Version 1.0.0 is now production-ready with full async support and many other improvements.

Installation

Basic Installation

pip install apifrom

Installation with Optional Dependencies

# With database support (SQLAlchemy, PostgreSQL, MySQL, SQLite)
pip install "apifrom[database]"

# With caching support (Redis)
pip install "apifrom[cache]"

# With monitoring support (Prometheus)
pip install "apifrom[monitoring]"

# With development tools
pip install "apifrom[dev]"

# With all optional dependencies
pip install "apifrom[all]"

Quick Start

Creating Your First API

Create a file named app.py:

from apifrom import API, api

# Create an API instance
app = API(
    title="My First API",
    description="A simple API created with APIFromAnything",
    version="1.0.0"
)

# Define an API endpoint
@api(route="/hello/{name}", method="GET")
def hello(name: str, greeting: str = "Hello") -> dict:
    """Say hello to someone."""
    return {"message": f"{greeting}, {name}!"}

# Run the API server
if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Run your API:

python app.py

Visit http://localhost:8000/hello/world in your browser or use curl:

curl http://localhost:8000/hello/world

You should see:

{"message": "Hello, world!"}

Creating an Async API

APIFromAnything has full support for async/await. Here’s how to create an async API:

from apifrom import API, api
import asyncio

app = API(
    title="Async API Example",
    description="An async API created with APIFromAnything",
    version="1.0.0"
)

@api(route="/async-hello/{name}", method="GET")
async def async_hello(name: str, greeting: str = "Hello") -> dict:
    """Say hello to someone asynchronously."""
    # Simulate an async operation
    await asyncio.sleep(1)
    return {"message": f"{greeting}, {name}!", "async": True}

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8000)

Testing Your API

Test the API using curl:

curl "http://localhost:8000/hello/World?greeting=Hi"
# {"message": "Hi, World!"}

Or visit the automatic Swagger documentation at http://localhost:8000/docs.

Core Concepts

API Instance

The API class is the main entry point for creating an API:

from apifrom import API

app = API(
    title="My API",
    description="API description",
    version="1.0.0",
    docs_url="/docs",  # URL for Swagger documentation
    openapi_url="/openapi.json",  # URL for OpenAPI schema
    redoc_url="/redoc",  # URL for ReDoc documentation
    debug=True  # Enable debug mode
)

API Decorator

The @api decorator is used to define API endpoints:

from apifrom import api

@api(
    route="/users/{user_id}",  # URL path with path parameters
    method="GET",  # HTTP method (GET, POST, PUT, DELETE, etc.)
    tags=["users"],  # Tags for documentation
    summary="Get a user",  # Summary for documentation
    description="Get a user by ID",  # Description for documentation
    response_model=dict,  # Expected response model
    status_code=200,  # HTTP status code
    deprecated=False,  # Mark as deprecated
    include_in_schema=True  # Include in OpenAPI schema
)
def get_user(user_id: int) -> dict:
    # Function implementation
    return {"id": user_id, "name": "John Doe"}

Request and Response

APIFromAnything automatically handles request parsing and response serialization:

from apifrom import API, api
from apifrom.core.request import Request
from apifrom.core.response import Response

app = API()

@api(route="/echo", method="POST")
def echo(request: Request) -> Response:
    """Echo the request body."""
    # Access request data
    body = request.json()
    headers = request.headers
    query_params = request.query_params
    path_params = request.path_params
    
    # Create a custom response
    return Response(
        content=body,
        status_code=200,
        headers={"X-Custom-Header": "Value"}
    )

Path Parameters

Path parameters are defined in the route path and automatically parsed:

@api(route="/users/{user_id}/posts/{post_id}", method="GET")
def get_user_post(user_id: int, post_id: int) -> dict:
    """Get a post by a specific user."""
    return {"user_id": user_id, "post_id": post_id}

Query Parameters

Query parameters are automatically parsed from the URL:

@api(route="/search", method="GET")
def search(query: str, page: int = 1, limit: int = 10) -> dict:
    """Search for items."""
    return {
        "query": query,
        "page": page,
        "limit": limit,
        "results": []
    }

Request Body

Request body is automatically parsed based on the function parameters:

from typing import List, Dict, Optional

@api(route="/users", method="POST")
def create_user(
    name: str,
    email: str,
    age: int,
    tags: List[str] = [],
    metadata: Dict[str, str] = {},
    address: Optional[str] = None
) -> dict:
    """Create a new user."""
    return {
        "id": 123,
        "name": name,
        "email": email,
        "age": age,
        "tags": tags,
        "metadata": metadata,
        "address": address
    }

Type Validation

APIFromAnything automatically validates input types:

from enum import Enum
from typing import List, Optional
from pydantic import BaseModel, Field, EmailStr

class UserRole(str, Enum):
    ADMIN = "admin"
    USER = "user"
    GUEST = "guest"

class Address(BaseModel):
    street: str
    city: str
    country: str
    postal_code: str

class User(BaseModel):
    name: str = Field(..., min_length=2, max_length=50)
    email: EmailStr
    age: int = Field(..., ge=18, le=120)
    role: UserRole = UserRole.USER
    tags: List[str] = []
    address: Optional[Address] = None

@api(route="/users", method="POST")
def create_user(user: User) -> dict:
    """Create a new user."""
    return user.dict()

Plugin System

The plugin system allows you to extend and customize the functionality of your API:

from apifrom import API
from apifrom.plugins import LoggingPlugin

# Create an API instance
app = API(title="My API")

# Create and register a logging plugin
logging_plugin = LoggingPlugin(
    log_request_body=True,
    log_response_body=True
)
app.plugin_manager.register(logging_plugin)

Plugins can:

  • Process requests before they are handled by the API

  • Process responses before they are sent to the client

  • Handle errors that occur during request processing

  • React to events emitted by the API

  • Register callbacks for specific hooks

For more information about the plugin system, see the Plugin System Documentation.

Middleware

Middleware allows you to process requests and responses:

from apifrom import API
from apifrom.middleware import Middleware
from apifrom.core.request import Request
from apifrom.core.response import Response
from typing import Callable, Awaitable

# Create a custom middleware
class LoggingMiddleware(Middleware):
    async def dispatch(
        self,
        request: Request,
        call_next: Callable[[Request], Awaitable[Response]]
    ) -> Response:
        print(f"Request: {request.method} {request.url.path}")
        
        # Process the request through the next middleware or endpoint
        response = await call_next(request)
        
        print(f"Response: {response.status_code}")
        return response

# Create an API instance
app = API()

# Add middleware to the API
app.add_middleware(LoggingMiddleware())

Error Handling

APIFromAnything provides comprehensive error handling:

from apifrom import API, api
from apifrom.middleware import ErrorHandlingMiddleware
from apifrom.exceptions import BadRequestError, NotFoundError

# Create an API instance
app = API()

# Add error handling middleware
app.add_middleware(
    ErrorHandlingMiddleware(
        debug=True,  # Include debug information in development
        include_traceback=True,  # Include tracebacks in error responses
        log_exceptions=True  # Log exceptions to the console
    )
)

@api(route="/users/{user_id}", method="GET")
def get_user(user_id: int):
    if user_id <= 0:
        raise BadRequestError(message="User ID must be positive")
    
    user = find_user(user_id)
    if not user:
        raise NotFoundError(
            message=f"User with ID {user_id} not found",
            details={"user_id": user_id}
        )
    
    return user

Next Steps

Now that you’ve learned the basics of APIFromAnything, you can explore more advanced features:

For more examples, check the /examples directory in the repository.