Skip to Content
SDKConnectorParameter Binding

Parameter Binding

The bind parameter injects infrastructure dependencies (database connections, configuration, auth context) into your endpoint functions without exposing them in the remote function signature.

Basic Usage

bind_basic.py
from rhesis.sdk import endpoint

# Bind a static configuration object
@endpoint(
    bind={
        "config": AppConfig()  # Evaluated once at decoration time
    }
)
def process_request(config, input: str) -> dict:
    """config is injected automatically, only input appears in remote signature."""
    return {"output": f"Using {config.api_url}: {input}"}

Late Binding with Callables

Use callables (lambdas or functions) for dependencies that should be evaluated fresh on each call:

bind_callable.py
@endpoint(
    bind={
        "db": lambda: get_db_session(),  # Fresh connection per call
        "user": lambda: get_current_user_context(),  # Runtime context
    }
)
async def authenticated_query(db, user, input: str) -> dict:
    """db and user are injected, only input appears in remote signature."""
    if not user.is_authenticated:
        return {"output": "Unauthorized"}

    results = db.query_for_user(user.id, input)
    return {"output": format_results(results)}

Key Concepts

Excluded from Remote Signature

Bound parameters don’t appear in the registered function signature, so remote tests only need to provide business logic parameters:

# Function definition @endpoint(bind={"db": lambda: get_db(), "config": AppConfig()}) def query_data(db, config, input: str, session_id: str = None): ... # Remote signature (what tests see) query_data(input: str, session_id: str = None)

Evaluation Timing

  • Static values: Evaluated once at decoration time
  • Callables: Evaluated fresh on each function call
bind_timing.py
config = AppConfig()  # Created once

@endpoint(
    bind={
        "config": config,  # Same instance every call
        "db": lambda: get_db(),  # Fresh connection every call
    }
)
def my_endpoint(config, db, input: str):
    ...

No Override

Bound parameters won’t override explicitly provided values:

bind_no_override.py
@endpoint(bind={"db": lambda: get_default_db()})
def query(db, input: str):
    return {"output": db.query(input)}

# Explicitly provide db - uses provided value, not bound value
result = query(db=custom_db, input="test")

Resource Cleanup

The SDK automatically handles cleanup for generator-based dependencies:

bind_cleanup.py
def get_db():
    """Generator that yields database session with auto-cleanup."""
    with database.get_session() as session:
        yield session
        # Cleanup happens automatically after function execution

@endpoint(
    bind={
        "db": get_db,  # Pass the generator function
    }
)
async def query_with_cleanup(db, input: str) -> dict:
    # Database connection is automatically closed after execution
    return {"output": db.query(input)}

When to Use bind vs Framework DI

Use bind for SDK endpoints that need the same dependencies in local execution and remote tests, or when working outside a web framework. Use framework DI (e.g., FastAPI’s Depends()) for HTTP endpoints where the framework manages the request lifecycle. The two can coexist on the same dependency:

bind_with_fastapi.py
from fastapi import FastAPI, Depends
from rhesis.sdk import endpoint

app = FastAPI()

# FastAPI endpoint with Depends
@app.post("/chat")
def fastapi_chat(
    input: str,
    db = Depends(get_db),  # FastAPI DI
):
    return process_chat(db, input)

# SDK endpoint with bind for remote testing
@endpoint(bind={"db": lambda: get_db()})  # SDK bind
def sdk_chat(db, input: str):
    return process_chat(db, input)

Next steps