FrappeAPI, FastAPI for Frappe Framework
Project description
FrappeAPI
Better APIs for Frappe.
FrappeAPI brings FastAPI-style routing and validation to the Frappe Framework. Define endpoints with type hints, get automatic validation and documentation.
Documentation
Installation
pip install frappeapi
Quick Start
from frappeapi import FrappeAPI
app = FrappeAPI()
@app.get()
def hello(name: str = "World"):
return {"message": f"Hello, {name}!"}
Examples
Path Parameters
Enable FastAPI-style paths for cleaner URLs:
from frappeapi import FrappeAPI
app = FrappeAPI(fastapi_path_format=True)
@app.get("/items/{item_id}")
def get_item(item_id: str):
return {"id": item_id}
# GET /api/items/abc123
# Response: {"id": "abc123"}
Multiple path parameters:
@app.get("/users/{user_id}/orders/{order_id}")
def get_user_order(user_id: str, order_id: int):
return {"user_id": user_id, "order_id": order_id}
# GET /api/users/john/orders/42
# Response: {"user_id": "john", "order_id": 42}
Combine path and query parameters:
@app.get("/products/{category}")
def list_products(
category: str, # Path parameter
sort_by: str = "name", # Query parameter
limit: int = 10 # Query parameter
):
return {"category": category, "sort_by": sort_by, "limit": limit}
# GET /api/products/electronics?sort_by=price&limit=20
Query Parameters
Automatic type parsing:
@app.get()
def get_product_details(
product_id: int,
unit_price: float,
in_stock: bool
):
return {
"product_id": product_id, # "123" -> 123
"unit_price": unit_price, # "9.99" -> 9.99
"in_stock": in_stock # "true" -> True
}
Optional parameters with defaults:
@app.get()
def list_products(
category: str = "all",
page: int = 1,
search: str | None = None
):
return {"category": category, "page": page, "search": search}
Enum parameters:
from enum import Enum
class OrderStatus(str, Enum):
pending = "pending"
processing = "processing"
completed = "completed"
@app.get()
def list_orders(status: OrderStatus = OrderStatus.pending):
return {"status": status}
List parameters:
from frappeapi import Query
@app.get()
def search_products(
tags: List[str] = Query(default=[]),
categories: List[int] = Query(default=[])
):
return {"tags": tags, "categories": categories}
# GET ?tags=electronics&tags=sale&categories=1&categories=2
# Response: {"tags": ["electronics", "sale"], "categories": [1, 2]}
Aliased parameters:
from typing import Annotated
from frappeapi import Query
@app.get()
def search_items(
search_text: Annotated[str, Query(alias="q")] = "",
page_number: Annotated[int, Query(alias="p")] = 1
):
return {"search": search_text, "page": page_number}
# GET ?q=laptop&p=2
Query parameter models:
from pydantic import BaseModel, Field
class ProductFilter(BaseModel):
search: str | None = None
category: str = "all"
min_price: float = Field(0, ge=0)
in_stock: bool = True
@app.get()
def filter_products(filters: Annotated[ProductFilter, Query()]):
return filters
Request Body
Single model:
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str = Field(..., min_length=1, max_length=50)
description: str | None = None
price: float = Field(..., gt=0)
@app.post()
def create_item(item: Item):
return item
Multiple body parameters:
class User(BaseModel):
username: str
email: str
class Item(BaseModel):
name: str
price: float
@app.post()
def create_user_item(user: User, item: Item):
return {"user": user, "item": item}
# Request body:
# {
# "user": {"username": "john", "email": "john@example.com"},
# "item": {"name": "Laptop", "price": 999.99}
# }
Nested models:
from pydantic import HttpUrl
class Image(BaseModel):
url: HttpUrl
name: str
class Product(BaseModel):
name: str
price: float
images: List[Image]
@app.post()
def create_product(product: Product):
return product
Form Data
from typing import Annotated
from frappeapi import Form
@app.post()
def create_user_profile(
username: Annotated[str, Form()],
email: Annotated[str, Form()],
bio: Annotated[str | None, Form()] = None
):
return {"username": username, "email": email, "bio": bio}
File Uploads
Small files (in-memory):
from typing import Annotated
from frappeapi import File, Form
@app.post()
def upload_document(
file: Annotated[bytes, File()],
description: Annotated[str | None, Form()] = None
):
return {"file_size": len(file), "description": description}
Large files (streamed):
from frappeapi import UploadFile
@app.post()
def upload_large_file(file: UploadFile):
return {
"filename": file.filename,
"content_type": file.content_type
}
Response Models
Filter response data:
class UserResponse(BaseModel):
id: int
username: str
email: str
@app.get(response_model=UserResponse)
def get_user(user_id: int):
return {
"id": user_id,
"username": "john_doe",
"email": "john@example.com",
"password": "secret" # Filtered out
}
List responses:
class Product(BaseModel):
id: int
name: str
price: float
@app.get(response_model=List[Product])
def list_products():
return [
{"id": 1, "name": "Laptop", "price": 999.99},
{"id": 2, "name": "Mouse", "price": 24.99}
]
Error Handling
Raise HTTP exceptions:
from frappeapi.exceptions import HTTPException
@app.get()
def get_item(item_id: int):
if item_id < 0:
raise HTTPException(status_code=400, detail="Item ID must be positive")
return {"id": item_id}
Custom exception handlers:
from frappeapi import JSONResponse, Request
class ItemNotFound(Exception):
def __init__(self, item_id: int):
self.item_id = item_id
@app.exception_handler(ItemNotFound)
def item_not_found_handler(request: Request, exc: ItemNotFound):
return JSONResponse(
status_code=404,
content={"error": "ITEM_NOT_FOUND", "detail": f"Item {exc.item_id} not found"}
)
Override validation error handler:
from frappeapi.exceptions import RequestValidationError
@app.exception_handler(RequestValidationError)
def validation_error_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=422,
content={
"error": "VALIDATION_ERROR",
"details": [{"field": e["loc"][-1], "message": e["msg"]} for e in exc.errors()]
}
)
Header Parameters
from typing import Annotated
from frappeapi import Header
@app.get()
def get_user_info(
user_agent: Annotated[str, Header()],
x_custom_header: Annotated[str, Header()]
):
return {"user_agent": user_agent, "custom_header": x_custom_header}
# Headers: User-Agent, X-Custom-Header (hyphen converted to underscore)
Field Validation
String validation:
class Product(BaseModel):
name: str = Field(min_length=3, max_length=50)
sku: str = Field(pattern="^[A-Z]{2}-[0-9]{4}$") # Format: XX-0000
Numeric validation:
class Order(BaseModel):
quantity: int = Field(gt=0, le=100)
unit_price: float = Field(gt=0)
discount_percent: float = Field(ge=0, le=100)
Version Compatibility
FrappeAPI automatically detects your Frappe version:
| Frappe Version | Support |
|---|---|
| v14.x | Stable |
| v15.x | Stable |
| v16.x | Beta |
Check detected version:
import frappeapi
print(frappeapi.get_detected_frappe_version()) # Returns: 14, 15, or 16
Documentation
FrappeAPI follows FastAPI's interface. For detailed information, see FastAPI's documentation.
Roadmap
Frappe Versions
- Frappe V14 support
- Frappe V15 support
- Frappe V16 support (develop branch)
Methods
-
app.get(...) -
app.post(...) -
app.put(...) -
app.patch(...) -
app.delete(...)
Query Parameters
- Automatic type parsing based on type hints
- Required parameters (
needy: str,needy: str = ...) - Optional parameters with defaults (
skip: int = 0) - Optional parameters without defaults (
limit: int | None = None) - Enum support
- Boolean parameters
- List parameters (
?q=foo&q=bar) - Aliased parameters (
Query(alias="query")) - Query parameter models
- Automatic documentation generation
Body Parameters
- Pydantic model body (
item: Item) - Multiple body parameters
- Singular values with
Body() - Embed body parameter
- Nested models
- Automatic type parsing
Header Parameters
- Basic header support
- Header parameter models
- Duplicate headers
- Forbid extra headers
Cookie Parameters
- Cookie parameter support
Form Data
- Form fields with
Form() - Multiple form fields
- Form data as Pydantic model
- Forbid extra form fields
File Uploads
- Small files with
File() - Large files with
UploadFile - Optional file uploads
- Multiple file uploads
Error Handling
- HTTPException
- RequestValidationError
- ResponseValidationError
- Custom exception handlers
- Override default handlers
- Frappe transaction management
Response Models
-
response_modelparameter - Return type annotations
- Output filtering
-
response_modeltakes precedence over return type
Validation
- String validations (
min_length,max_length,pattern) - Numeric validations (
gt,ge,lt,le) - Metadata (
title,description,deprecated) -
include_in_schema
Planned
- Rate limiting
- Dependencies
- Middleware
- Debugging capabilities
- Dotted path parameters
Related
- PR #23135: Type hints for API functions
- PR #22300: Enhanced
frappe.whitelist() - PR #19029: Type safety improvements
- Issue #14905: API documentation discussion
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 frappeapi-0.3.2.tar.gz.
File metadata
- Download URL: frappeapi-0.3.2.tar.gz
- Upload date:
- Size: 886.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4c2b25e4ed8f7b3626e4592e541bfb8c8e12802a8fbb959f2834a8028af1e70b
|
|
| MD5 |
a5186be3657e39727701b3d1983758a6
|
|
| BLAKE2b-256 |
7cdbce0d0d4c9fa9c14eabdaf487aa29c67d9d4f7b09297f1a8f7469050be7c8
|
File details
Details for the file frappeapi-0.3.2-py3-none-any.whl.
File metadata
- Download URL: frappeapi-0.3.2-py3-none-any.whl
- Upload date:
- Size: 1.2 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"22.04","id":"jammy","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5564c4258f4887d233a0f07df5f29b9a288164caaed2b4a980627763953f87f
|
|
| MD5 |
a7aa36ac4c811d49a7ae90e559975f9a
|
|
| BLAKE2b-256 |
d5a101df1163fdfd530f164f695198d16b307dc7789c033cade48b6e472a31b8
|