Application Hostingยถ
Time to read: 15 minutes
Neuroglia's hosting infrastructure provides enterprise-grade application lifecycle management for building production-ready microservices. The WebApplicationBuilder is the central component that handles configuration, dependency injection, and application startup.
๐ฏ What & Whyยถ
What is Application Hosting?ยถ
Application hosting in Neuroglia manages the complete lifecycle of a web application:
- Configuration: Loading settings, environment variables
- Dependency Injection: Registering and resolving services
- Controller Discovery: Finding and mounting API controllers
- Background Services: Running tasks alongside the web server
- Lifecycle Management: Startup, running, graceful shutdown
- Observability: Health checks, metrics, tracing
Why Use Neuroglia Hosting?ยถ
Without Neuroglia Hosting:
# โ Manual setup - repetitive and error-prone
from fastapi import FastAPI
import uvicorn
app = FastAPI()
# Manually register each controller
from api.controllers.users import router as users_router
from api.controllers.orders import router as orders_router
app.include_router(users_router, prefix="/api/users")
app.include_router(orders_router, prefix="/api/orders")
# No DI - manual instantiation
database = Database(connection_string=os.getenv("DB_CONN"))
user_service = UserService(database)
# No lifecycle management
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
With Neuroglia Hosting:
# โ
Clean, declarative, automatic
from neuroglia.hosting import WebApplicationBuilder
builder = WebApplicationBuilder()
# Auto-discover and register controllers
builder.add_controllers(["api.controllers"])
# DI handles instantiation
builder.services.add_scoped(Database)
builder.services.add_scoped(UserService)
# Built-in lifecycle management
app = builder.build()
app.run()
๐ Getting Startedยถ
Simple Mode (Basic Applications)ยถ
For straightforward applications with single API:
from neuroglia.hosting import WebApplicationBuilder
def create_app():
builder = WebApplicationBuilder()
# Register services
builder.services.add_scoped(OrderRepository)
builder.services.add_scoped(OrderService)
# Auto-discover controllers
builder.add_controllers(["api.controllers"])
# Build and return
host = builder.build()
return host
if __name__ == "__main__":
app = create_app()
app.run(host="0.0.0.0", port=8000)
Advanced Mode (Production Applications)ยถ
For production apps with observability, multi-app support:
from neuroglia.hosting import WebApplicationBuilder
from neuroglia.hosting.abstractions import ApplicationSettings
def create_app():
# Load configuration
app_settings = ApplicationSettings()
# Advanced features enabled automatically
builder = WebApplicationBuilder(app_settings)
# Register services
builder.services.add_scoped(OrderRepository)
builder.services.add_scoped(EmailService)
# Multi-app support with prefixes
builder.add_controllers(["api.controllers"], prefix="/api")
builder.add_controllers(["admin.controllers"], prefix="/admin")
# Add background services
builder.services.add_hosted_service(CleanupService)
# Build with lifecycle management
app = builder.build_app_with_lifespan(
title="Mario's Pizzeria API",
version="1.0.0"
)
return app
if __name__ == "__main__":
import uvicorn
app = create_app()
uvicorn.run(app, host="0.0.0.0", port=8000)
๐๏ธ Core Componentsยถ
WebApplicationBuilderยถ
The main builder for creating applications:
from neuroglia.hosting import WebApplicationBuilder
# Simple mode
builder = WebApplicationBuilder()
# Advanced mode
from neuroglia.hosting.abstractions import ApplicationSettings
app_settings = ApplicationSettings()
builder = WebApplicationBuilder(app_settings)
Key Properties:
services: ServiceCollection for DI registrationapp: The FastAPI application instance (after build)app_settings: Application configuration
Key Methods:
add_controllers(modules, prefix): Register controllersbuild(): Build host (returns WebHost or EnhancedWebHost)build_app_with_lifespan(title, version): Build FastAPI app with lifecycleuse_controllers(): Mount controllers on app
Controller Registrationยถ
Automatically discover and register controllers:
# Single module
builder.add_controllers(["api.controllers"])
# Multiple modules
builder.add_controllers([
"api.controllers.orders",
"api.controllers.customers",
"api.controllers.menu"
])
# With custom prefix
builder.add_controllers(["api.controllers"], prefix="/api/v1")
# Multiple apps with different prefixes
builder.add_controllers(["api.controllers"], prefix="/api")
builder.add_controllers(["admin.controllers"], prefix="/admin")
Hosted Servicesยถ
Background services that run alongside your application:
from neuroglia.hosting.abstractions import HostedService
class CleanupService(HostedService):
"""Background service for cleanup tasks."""
async def start_async(self):
"""Called on application startup."""
self.running = True
while self.running:
await self.cleanup_old_orders()
await asyncio.sleep(3600) # Run every hour
async def stop_async(self):
"""Called on application shutdown."""
self.running = False
async def cleanup_old_orders(self):
# Cleanup logic
pass
# Register hosted service
builder.services.add_hosted_service(CleanupService)
Application Settingsยถ
Configuration management:
from neuroglia.hosting.abstractions import ApplicationSettings
from pydantic import Field
class MyAppSettings(ApplicationSettings):
"""Custom application settings."""
database_url: str = Field(default="mongodb://localhost:27017")
redis_url: str = Field(default="redis://localhost:6379")
api_key: str = Field(default="", env="API_KEY")
debug: bool = Field(default=False)
# Load settings (from environment variables)
app_settings = MyAppSettings()
# Use in builder
builder = WebApplicationBuilder(app_settings)
# Access in services via DI
class OrderService:
def __init__(self, settings: MyAppSettings):
self.db_url = settings.database_url
๐ก Real-World Example: Mario's Pizzeriaยถ
Complete application setup:
from neuroglia.hosting import WebApplicationBuilder
from neuroglia.hosting.abstractions import ApplicationSettings
from neuroglia.dependency_injection import ServiceLifetime
from application.settings import PizzeriaSettings
def create_app():
# Load configuration
settings = PizzeriaSettings()
# Create builder with settings
builder = WebApplicationBuilder(settings)
# Register domain repositories
builder.services.add_scoped(IOrderRepository, MongoOrderRepository)
builder.services.add_scoped(ICustomerRepository, MongoCustomerRepository)
builder.services.add_scoped(IMenuRepository, MongoMenuRepository)
# Register application services
builder.services.add_scoped(OrderService)
builder.services.add_scoped(CustomerService)
# Register infrastructure
builder.services.add_singleton(EmailService)
builder.services.add_singleton(PaymentService)
# Configure core services
Mediator.configure(builder, ["application.commands", "application.queries"])
Mapper.configure(builder, ["application.mapping", "api.dtos"])
# Add SubApp with controllers
builder.add_sub_app(
SubAppConfig(
path="/api",
name="api",
controllers=[
"api.controllers.orders",
"api.controllers.customers",
"api.controllers.menu"
]
)
)
# Add background services
builder.services.add_hosted_service(OrderCleanupService)
builder.services.add_hosted_service(MetricsCollectorService)
# Build application with lifecycle management
app = builder.build_app_with_lifespan(
title="Mario's Pizzeria API",
version="1.0.0",
description="Pizza ordering and management system"
)
return app
if __name__ == "__main__":
import uvicorn
app = create_app()
uvicorn.run(
app,
host="0.0.0.0",
port=8000,
log_level="info"
)
๐ง Advanced Featuresยถ
Multi-Application Architectureยถ
Host multiple FastAPI applications in one process:
from neuroglia.hosting import WebApplicationBuilder
from fastapi import FastAPI
# Create builder with settings
builder = WebApplicationBuilder(app_settings)
# Create custom sub-applications
api_app = FastAPI(title="Public API")
admin_app = FastAPI(title="Admin Panel")
# Register controllers to specific apps
builder.add_controllers(
["api.controllers"],
app=api_app,
prefix="/api"
)
builder.add_controllers(
["admin.controllers"],
app=admin_app,
prefix="/admin"
)
# Build host that manages both apps
host = builder.build()
# host.app has both /api and /admin mounted
Exception Handlingยถ
Global exception handling middleware:
from neuroglia.hosting.web import ExceptionHandlingMiddleware
# Automatically included in WebApplicationBuilder
# Catches exceptions and formats responses
# Custom error handling
from fastapi import HTTPException
@app.exception_handler(HTTPException)
async def custom_http_exception_handler(request, exc):
return {
"error": exc.detail,
"status_code": exc.status_code
}
Lifecycle Hooksยถ
React to application lifecycle events:
from contextlib import asynccontextmanager
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan manager."""
# Startup
print("Application starting...")
await database.connect()
yield # Application running
# Shutdown
print("Application shutting down...")
await database.disconnect()
# Use in build
app = builder.build_app_with_lifespan(
title="My App",
lifespan=lifespan
)
๐งช Testingยถ
Testing with Builderยถ
import pytest
from neuroglia.hosting import WebApplicationBuilder
@pytest.fixture
def test_app():
"""Create test application."""
builder = WebApplicationBuilder()
# Use in-memory implementations
builder.services.add_scoped(IOrderRepository, InMemoryOrderRepository)
builder.add_controllers(["api.controllers"])
app = builder.build_app_with_lifespan(title="Test App")
return app
async def test_create_order(test_app):
"""Test order creation endpoint."""
from fastapi.testclient import TestClient
client = TestClient(test_app)
response = client.post("/api/orders", json={
"customer_id": "123",
"items": [{"pizza": "Margherita", "quantity": 1}]
})
assert response.status_code == 201
assert "order_id" in response.json()
โ ๏ธ Common Mistakesยถ
1. Not Building the Appยถ
# โ WRONG: Forgot to build
builder = WebApplicationBuilder()
builder.add_controllers(["api.controllers"])
# Missing: app = builder.build()
# โ
RIGHT: Build before running
builder = WebApplicationBuilder()
builder.add_controllers(["api.controllers"])
app = builder.build()
app.run()
2. Registering Controllers After Buildยถ
# โ WRONG: Adding controllers after build
builder = WebApplicationBuilder()
app = builder.build()
builder.add_controllers(["api.controllers"]) # Too late!
# โ
RIGHT: Register before build
builder = WebApplicationBuilder()
builder.add_controllers(["api.controllers"])
app = builder.build()
3. Mixing Simple and Advanced Featuresยถ
# โ WRONG: Using advanced features without app_settings
builder = WebApplicationBuilder() # Simple mode
builder.add_controllers(["api.controllers"], prefix="/api")
builder.add_controllers(["admin.controllers"], prefix="/admin")
# Advanced features may not work properly
# โ
RIGHT: Use app_settings for advanced features
app_settings = ApplicationSettings()
builder = WebApplicationBuilder(app_settings) # Advanced mode
builder.add_controllers(["api.controllers"], prefix="/api")
builder.add_controllers(["admin.controllers"], prefix="/admin")
๐ซ When NOT to Useยถ
Skip Neuroglia hosting when:
- Serverless Functions: AWS Lambda, Azure Functions (stateless)
- Minimal APIs: Single endpoint, no DI needed
- Non-Web Applications: CLI tools, batch jobs
- Existing FastAPI App: Already have complex setup
For these cases, use FastAPI directly or other appropriate tools.
๐ Key Takeawaysยถ
- WebApplicationBuilder: Central component for app configuration
- Two Modes: Simple (basic) and Advanced (production features)
- Auto-Discovery: Controllers automatically found and registered
- Lifecycle Management: Startup, running, graceful shutdown
- Background Services: HostedService for concurrent tasks
- Dependency Injection: Integrated ServiceCollection
๐ Related Documentationยถ
- Getting Started - Basic usage
- Tutorial Part 1 - Complete setup guide
- Dependency Injection - Service registration
- MVC Controllers - Building controllers
- Observability - Monitoring and tracing
๐ API Referenceยถ
WebApplicationBuilderยถ
class WebApplicationBuilder:
def __init__(
self,
app_settings: Optional[Union[ApplicationSettings, ApplicationSettingsWithObservability]] = None
):
"""Initialize builder with optional settings."""
@property
def services(self) -> ServiceCollection:
"""Get the service collection for DI registration."""
@property
def app(self) -> Optional[FastAPI]:
"""Get the FastAPI app instance (after build)."""
@property
def app_settings(self) -> Optional[ApplicationSettings]:
"""Get the application settings."""
def add_controllers(
self,
modules: List[str],
app: Optional[FastAPI] = None,
prefix: str = ""
) -> ServiceCollection:
"""Register controllers from specified modules."""
def build(self, auto_mount_controllers: bool = True) -> WebHostBase:
"""Build the host."""
def build_app_with_lifespan(
self,
title: str = "Neuroglia Application",
version: str = "1.0.0",
description: str = "",
lifespan: Optional[Callable] = None
) -> FastAPI:
"""Build FastAPI app with lifecycle management."""
def use_controllers(self):
"""Mount controllers on the application."""
HostedServiceยถ
class HostedService(ABC):
"""Base class for background services."""
@abstractmethod
async def start_async(self):
"""Called on application startup."""
@abstractmethod
async def stop_async(self):
"""Called on application shutdown."""
ApplicationSettingsยถ
class ApplicationSettings:
"""Base application configuration."""
# Override with Pydantic Fields
app_name: str = "My Application"
environment: str = "development"
debug: bool = False
Next: Observability โ