Appearance
FastAPI Endpoint Generation
The haskoning_equation package can automatically expose Python functions as POST API endpoints using FastAPI, based on configuration in pyproject.toml. This eliminates boilerplate code and ensures type-safe request/response handling.
Features
- Automatically registers functions as
/module_name/function_nameendpoints - Type-safe request/response validation with Pydantic
- Function-based only — classes are not supported (wrap your class in a function to expose)
- Only functions with complete type annotations are exposed
- Skips endpoints already registered in FastAPI
- Skips functions with missing parameter or return annotations
- Support for FastAPI
Requestparameter to access request state, headers, and client info - Automatic conversion of
io.BytesIOreturn types toStreamingResponsefor file downloads - Automatic wrapping of non-
BaseModelresponses as{ "result": ... }
Prerequisites
haskoning_equationinstalledfastapianduvicornavailable (installed automatically if missing)- Valid
pyproject.tomlconfiguration with[tool.faster_api]
Configuration
pyproject.toml Setup
toml
[tool.faster_api]
include_modules = ["src.my_module"]
exclude_modules = ["src.my_module.internal"]
# Optional CORS settings
cors_settings = {
allow_origins = ["https://example.com"],
allow_methods = ["GET", "POST"],
allow_headers = ["Authorization"],
allow_credentials = true
}If cors_settings is not configured, CORS is allowed from all origins by default.
Basic Usage
Example Function
python
# src/my_module.py
def calculate_index(a: int, b: float) -> float:
"""Calculate a simple index."""
return a * bThis function will automatically be exposed as a POST endpoint at /my_module/calculate_index.
Using Request Parameter
Access request-specific information (headers, state, client info) by adding a request: Request parameter:
python
from fastapi import Request
from pydantic import BaseModel
class InputModel(BaseModel):
a: int
b: int
class OutputModel(BaseModel):
result: int
user_id: str | None = None
def calculate_with_context(request: Request, input: InputModel) -> OutputModel:
"""Function that accesses request state set by middleware."""
# Access user information from request state (set by middleware)
user_id = getattr(request.state, "user_id", None)
# Your calculation logic
result = input.a + input.b
return OutputModel(result=result, user_id=user_id)The Request parameter is automatically injected by FastAPI and doesn't need to be included in the request body.
Returning Files with BytesIO
Functions that return io.BytesIO or Optional[io.BytesIO] are automatically converted to StreamingResponse for file downloads:
python
import io
from pydantic import BaseModel
class ExportRequest(BaseModel):
filename: str
data: list[dict]
def export_csv(request: ExportRequest) -> io.BytesIO:
"""Export data as CSV file."""
# Create CSV content
csv_content = "id,name,value\n"
for row in request.data:
csv_content += f"{row['id']},{row['name']},{row['value']}\n"
# Return as BytesIO - automatically converted to StreamingResponse
buffer = io.BytesIO(csv_content.encode('utf-8'))
buffer.filename = "sample.csv"
return bufferRunning the API
Standalone FastAPI App
Generate and run a standalone FastAPI app with all configured endpoints:
bash
poetry run faster-apiTo run a dummy function defined in the package for quick testing:
bash
poetry run faster-api --dummyThe API will be available at http://localhost:8000. Visit http://localhost:8000/docs to see and test available endpoints.
Attach to Existing FastAPI App
Integrate endpoint generation into an existing FastAPI application:
python
from fastapi import FastAPI
from haskoning_equation.faster_api import FasterAPI, FasterAPIConfig
app = FastAPI()
faster_api_config = FasterAPIConfig(include_modules=['haskoning_hycalc.api'])
faster_api = FasterAPI(app, faster_api_config)
faster_api.register_all_endpoints()Then run with:
bash
uvicorn app.main:app --reloadServing Static Files
To display images in CardStyle.image_url, include the image in your package and ensure the backend serves it.
Project Structure
Organize your project to include static files:
haskoning_hycalc/
├─ haskoning_hycalc/
│ ├─ __init__.py
│ ├─ static/
│ │ └─ images/
│ │ └─ driehoek.jpgURL Mapping
When using register_all_endpoints, the static directory is automatically mounted. Static files are served at: /your_package_name/static/...
Example: http://localhost:8000/haskoning_hycalc/static/images/driehoek.jpg
CardStyle with Image
python
from haskoning_equation.style import CardStyle
CardStyle(
title="Driehoek",
subtitle="Bereken zijdes of hoek van een rechthoekige driehoek",
icon="mdi-angle-acute",
min_inputs=2,
image_url="/haskoning_hycalc/static/images/driehoek.jpg",
)Frontend Usage
The frontend should prepend a configured API base URL (e.g., VITE_APP_API_BASE_URL). Normalize slashes to avoid double slashes or missing separators.
Best Practices
- Only put public, non-sensitive assets in
static/ - Use versioned paths if you need cache busting
- Keep file sizes reasonable for web delivery
Running Multiple Equation Apps
You can host multiple Equation applications in a single FastAPI instance:
python
from fastapi import FastAPI
from haskoning_equation import apply_equation_web_client_settings, EquationWebClientSettings
from haskoning_equation.faster_api import FasterAPI, FasterAPIConfig
app = FastAPI()
# Existing Nereda router
nereda_settings = EquationWebClientSettings(app_name="Nereda", route="/nereda")
app.include_router(nereda_router, prefix="/nereda", tags=["Nereda"])
# Add Hycalc endpoints
hycalc_settings = EquationWebClientSettings(app_name="Hycalc", route="/hycalc")
faster_api_config = FasterAPIConfig(
title="Hycalc",
include_modules=['haskoning_hycalc.api'],
equation_web_client_settings=[hycalc_settings]
)
faster_api = FasterAPI(app, faster_api_config)
faster_api.register_all_endpoints()
# Apply settings for both apps
apply_equation_web_client_settings(app, [nereda_settings, hycalc_settings])Each app will be available at its configured route:
- Nereda:
http://localhost:8000/nereda - Hycalc:
http://localhost:8000/hycalc
Troubleshooting
Function Not Exposed
- Ensure all parameters and return types have type annotations
- Check that the module is listed in
include_modules - Verify the module is not in
exclude_modules - Classes are not supported — wrap in a function
CORS Issues
- Configure
cors_settingsinpyproject.tomlto specify allowed origins - Default allows all origins if not configured
Static Files Not Loading
- Verify the static folder exists in your package
- Check the URL path matches your package name
- Ensure files are included in your package distribution