Skip to main content

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

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 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_model parameter
  • Return type annotations
  • Output filtering
  • response_model takes 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

Metadata

Release files for frappeapi 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for frappeapi 0.3.2
File Size Uploaded Provenance Actions
frappeapi-0.3.2.tar.gz 886.6 kB Without provenance Details for frappeapi-0.3.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for frappeapi 0.3.2
File Interpreter ABI Platform Actions
frappeapi-0.3.2-py3-none-any.whl Python 3 none any Details for frappeapi-0.3.2-py3-none-any.whl

Total release size: 2.1 MB

Release files / frappeapi-0.3.2.tar.gz

Download URL frappeapi-0.3.2.tar.gz
Size 886.6 kB
Tags Source
SHA-256 checksum
How to use checksums
4c2b25e4ed8f7b3626e4592e541bfb8c8e12802a8fbb959f2834a8028af1e70b
BLAKE2b-256 checksum
How to use checksums
7cdbce0d0d4c9fa9c14eabdaf487aa29c67d9d4f7b09297f1a8f7469050be7c8
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / frappeapi-0.3.2-py3-none-any.whl

Download URL frappeapi-0.3.2-py3-none-any.whl
Size 1.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
f5564c4258f4887d233a0f07df5f29b9a288164caaed2b4a980627763953f87f
BLAKE2b-256 checksum
How to use checksums
d5a101df1163fdfd530f164f695198d16b307dc7789c033cade48b6e472a31b8
Upload date
Uploaded using Trusted Publishing?
What is 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}

Security for frappeapi 0.3.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page