0
# Core Application
1
2
The Starlette application class is the central component that manages routing, middleware, lifecycle events, and configuration for your ASGI web application.
3
4
## Application Class
5
6
```python { .api }
7
from starlette.applications import Starlette
8
from starlette.routing import BaseRoute
9
from starlette.middleware import Middleware
10
from starlette.types import Lifespan
11
from typing import Sequence, Mapping, Callable, Any
12
13
class Starlette:
14
"""
15
Main application class for creating ASGI applications.
16
17
The Starlette application instance manages:
18
- Request routing
19
- Middleware stack
20
- Exception handling
21
- Lifecycle events (startup/shutdown)
22
- Application state
23
"""
24
25
def __init__(
26
self,
27
debug: bool = False,
28
routes: Sequence[BaseRoute] | None = None,
29
middleware: Sequence[Middleware] | None = None,
30
exception_handlers: Mapping[Any, Callable] | None = None,
31
on_startup: Sequence[Callable] | None = None,
32
on_shutdown: Sequence[Callable] | None = None,
33
lifespan: Lifespan[Any] | None = None,
34
) -> None:
35
"""
36
Initialize Starlette application.
37
38
Args:
39
debug: Enable debug mode with detailed error pages
40
routes: List of route definitions
41
middleware: List of middleware to apply
42
exception_handlers: Custom exception handlers
43
on_startup: Functions to run on application startup (deprecated)
44
on_shutdown: Functions to run on application shutdown (deprecated)
45
lifespan: Async context manager for application lifespan
46
"""
47
48
# Properties
49
@property
50
def routes(self) -> list[BaseRoute]:
51
"""List of application routes."""
52
53
@property
54
def router(self) -> Router:
55
"""Router instance handling request dispatch."""
56
57
@property
58
def state(self) -> State:
59
"""Application state object for sharing data."""
60
61
# Core methods
62
def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
63
"""Generate URL path for named route."""
64
65
def build_middleware_stack(self) -> ASGIApp:
66
"""Build the middleware stack around the application."""
67
68
# Route management
69
def mount(
70
self,
71
path: str,
72
app: ASGIApp,
73
name: str | None = None
74
) -> None:
75
"""Mount an ASGI application at the given path."""
76
77
def host(
78
self,
79
host: str,
80
app: ASGIApp,
81
name: str | None = None
82
) -> None:
83
"""Add host-based routing for the application."""
84
85
def add_route(
86
self,
87
path: str,
88
route: Callable[[Request], Awaitable[Response] | Response],
89
methods: list[str] | None = None,
90
name: str | None = None,
91
include_in_schema: bool = True,
92
) -> None:
93
"""Add an HTTP route to the application."""
94
95
def add_websocket_route(
96
self,
97
path: str,
98
route: Callable[[WebSocket], Awaitable[None]],
99
name: str | None = None,
100
) -> None:
101
"""Add a WebSocket route to the application."""
102
103
# Middleware management
104
def add_middleware(
105
self,
106
middleware_class: type,
107
*args: Any,
108
**kwargs: Any,
109
) -> None:
110
"""Add middleware to the application stack."""
111
112
# Exception handling
113
def add_exception_handler(
114
self,
115
exc_class_or_status_code: int | type[Exception],
116
handler: Callable,
117
) -> None:
118
"""Add custom exception handler."""
119
120
# Event handlers (deprecated - use lifespan instead)
121
def add_event_handler(
122
self,
123
event_type: str, # "startup" or "shutdown"
124
func: Callable,
125
) -> None:
126
"""Add event handler (deprecated)."""
127
128
# Deprecated decorators (will be removed in v1.0.0)
129
def exception_handler(
130
self,
131
exc_class_or_status_code: int | type[Exception]
132
) -> Callable:
133
"""Decorator for exception handlers (deprecated)."""
134
135
def route(
136
self,
137
path: str,
138
methods: list[str] | None = None,
139
name: str | None = None,
140
include_in_schema: bool = True,
141
) -> Callable:
142
"""Decorator for routes (deprecated)."""
143
144
def websocket_route(
145
self,
146
path: str,
147
name: str | None = None
148
) -> Callable:
149
"""Decorator for WebSocket routes (deprecated)."""
150
151
def middleware(self, middleware_type: str) -> Callable:
152
"""Decorator for middleware (deprecated)."""
153
```
154
155
## Basic Application Setup
156
157
### Simple Application
158
159
```python { .api }
160
from starlette.applications import Starlette
161
from starlette.responses import JSONResponse
162
from starlette.routing import Route
163
164
async def homepage(request):
165
return JSONResponse({"message": "Hello World"})
166
167
app = Starlette(
168
debug=True,
169
routes=[
170
Route("/", homepage),
171
]
172
)
173
```
174
175
### Application with Configuration
176
177
```python { .api }
178
from starlette.applications import Starlette
179
from starlette.config import Config
180
from starlette.middleware import Middleware
181
from starlette.middleware.cors import CORSMiddleware
182
from starlette.routing import Route
183
184
# Load configuration
185
config = Config(".env")
186
DEBUG = config("DEBUG", cast=bool, default=False)
187
188
# Configure middleware
189
middleware = [
190
Middleware(CORSMiddleware, allow_origins=["*"]),
191
]
192
193
# Configure exception handlers
194
def http_exception_handler(request, exc):
195
return JSONResponse(
196
{"error": exc.detail},
197
status_code=exc.status_code
198
)
199
200
exception_handlers = {
201
HTTPException: http_exception_handler,
202
}
203
204
# Create application
205
app = Starlette(
206
debug=DEBUG,
207
routes=routes,
208
middleware=middleware,
209
exception_handlers=exception_handlers,
210
)
211
```
212
213
## Application Lifecycle
214
215
### Startup and Shutdown Events (Deprecated)
216
217
```python { .api }
218
# Deprecated approach using on_startup/on_shutdown
219
async def startup():
220
print("Application is starting up...")
221
# Initialize database connections, load ML models, etc.
222
223
async def shutdown():
224
print("Application is shutting down...")
225
# Close database connections, cleanup resources, etc.
226
227
app = Starlette(
228
on_startup=[startup],
229
on_shutdown=[shutdown],
230
routes=routes,
231
)
232
```
233
234
### Modern Lifespan Management
235
236
```python { .api }
237
from contextlib import asynccontextmanager
238
from starlette.applications import Starlette
239
240
# Modern approach using lifespan context manager
241
@asynccontextmanager
242
async def lifespan(app: Starlette):
243
# Startup
244
print("Application starting up...")
245
# Initialize resources
246
app.state.database = await database.connect()
247
app.state.ml_models = await load_ml_models()
248
249
yield # Application runs here
250
251
# Shutdown
252
print("Application shutting down...")
253
# Cleanup resources
254
await app.state.database.disconnect()
255
await cleanup_ml_models(app.state.ml_models)
256
257
app = Starlette(
258
lifespan=lifespan,
259
routes=routes,
260
)
261
```
262
263
### Lifespan with Error Handling
264
265
```python { .api }
266
@asynccontextmanager
267
async def lifespan(app: Starlette):
268
# Startup phase
269
try:
270
# Initialize database
271
app.state.database = await database.connect()
272
273
# Initialize cache
274
app.state.cache = await cache.connect()
275
276
# Load configuration
277
app.state.config = await load_application_config()
278
279
print("Application startup complete")
280
281
except Exception as e:
282
print(f"Failed to start application: {e}")
283
raise
284
285
yield # Application is running
286
287
# Shutdown phase
288
try:
289
# Close connections gracefully
290
if hasattr(app.state, 'database'):
291
await app.state.database.disconnect()
292
293
if hasattr(app.state, 'cache'):
294
await app.state.cache.disconnect()
295
296
print("Application shutdown complete")
297
298
except Exception as e:
299
print(f"Error during shutdown: {e}")
300
301
app = Starlette(lifespan=lifespan, routes=routes)
302
```
303
304
## Application State
305
306
The application state object allows you to share data across your application.
307
308
```python { .api }
309
from starlette.datastructures import State
310
311
# Application state is automatically created
312
app = Starlette()
313
314
# Set state during lifespan or route handlers
315
@asynccontextmanager
316
async def lifespan(app: Starlette):
317
# Set application-level state
318
app.state.database = await connect_to_database()
319
app.state.config = load_config()
320
app.state.cache = {}
321
322
yield
323
324
await app.state.database.disconnect()
325
326
# Access state in route handlers
327
async def get_users(request):
328
database = request.app.state.database
329
users = await database.fetch_all("SELECT * FROM users")
330
return JSONResponse([dict(user) for user in users])
331
332
# Access state in middleware
333
class DatabaseMiddleware:
334
def __init__(self, app):
335
self.app = app
336
337
async def __call__(self, scope, receive, send):
338
if scope["type"] == "http":
339
# Access app state
340
database = scope["app"].state.database
341
# Add to request scope
342
scope["database"] = database
343
344
await self.app(scope, receive, send)
345
```
346
347
## URL Generation
348
349
```python { .api }
350
from starlette.routing import Route
351
352
# Named routes for URL generation
353
routes = [
354
Route("/", homepage, name="home"),
355
Route("/users/{user_id}", user_detail, name="user_detail"),
356
Route("/api/users/{user_id}/posts/{post_id}", post_detail, name="post_detail"),
357
]
358
359
app = Starlette(routes=routes)
360
361
# Generate URLs from application
362
home_url = app.url_path_for("home") # "/"
363
user_url = app.url_path_for("user_detail", user_id=123) # "/users/123"
364
post_url = app.url_path_for(
365
"post_detail",
366
user_id=123,
367
post_id=456
368
) # "/api/users/123/posts/456"
369
370
# Generate URLs in request handlers
371
async def some_endpoint(request):
372
# Access url_path_for through request
373
user_url = request.url_for("user_detail", user_id=123)
374
375
return JSONResponse({
376
"user_profile_url": str(user_url),
377
"home_url": str(request.url_for("home"))
378
})
379
```
380
381
## Dynamic Route Management
382
383
```python { .api }
384
# Add routes dynamically after application creation
385
app = Starlette()
386
387
# Add individual routes
388
app.add_route("/health", health_check, methods=["GET"])
389
app.add_websocket_route("/ws", websocket_endpoint)
390
391
# Mount sub-applications
392
from starlette.staticfiles import StaticFiles
393
app.mount("/static", StaticFiles(directory="static"), name="static")
394
395
# Mount API sub-application
396
api_app = Starlette(routes=api_routes)
397
app.mount("/api/v1", api_app, name="api_v1")
398
399
# Host-based routing
400
admin_app = Starlette(routes=admin_routes)
401
app.host("admin.example.com", admin_app, name="admin")
402
```
403
404
## Exception Handling
405
406
```python { .api }
407
from starlette.exceptions import HTTPException
408
from starlette.responses import JSONResponse, PlainTextResponse
409
410
# Global exception handlers
411
async def http_exception_handler(request, exc):
412
return JSONResponse(
413
{"error": exc.detail},
414
status_code=exc.status_code,
415
headers=exc.headers,
416
)
417
418
async def validation_exception_handler(request, exc):
419
return JSONResponse(
420
{"error": "Validation failed", "details": str(exc)},
421
status_code=422,
422
)
423
424
async def generic_exception_handler(request, exc):
425
return PlainTextResponse(
426
"Internal server error",
427
status_code=500,
428
)
429
430
# Configure exception handlers
431
exception_handlers = {
432
HTTPException: http_exception_handler,
433
ValueError: validation_exception_handler,
434
500: generic_exception_handler, # By status code
435
}
436
437
app = Starlette(
438
exception_handlers=exception_handlers,
439
routes=routes,
440
)
441
442
# Add exception handlers dynamically
443
app.add_exception_handler(KeyError, key_error_handler)
444
app.add_exception_handler(404, not_found_handler)
445
```
446
447
## Debug Mode
448
449
```python { .api }
450
# Enable debug mode for development
451
app = Starlette(debug=True, routes=routes)
452
453
# Debug mode provides:
454
# - Detailed error pages with stack traces
455
# - Automatic reloading on code changes (with --reload)
456
# - More verbose logging
457
458
# Control debug mode with environment variables
459
from starlette.config import Config
460
461
config = Config()
462
DEBUG = config("DEBUG", cast=bool, default=False)
463
464
app = Starlette(debug=DEBUG, routes=routes)
465
```
466
467
## Application Composition
468
469
```python { .api }
470
# Compose applications from multiple modules
471
from .api import api_app
472
from .admin import admin_app
473
from .auth import auth_app
474
475
# Main application
476
app = Starlette()
477
478
# Mount sub-applications
479
app.mount("/api", api_app, name="api")
480
app.mount("/admin", admin_app, name="admin")
481
app.mount("/auth", auth_app, name="auth")
482
483
# Add global middleware
484
app.add_middleware(CORSMiddleware, allow_origins=["*"])
485
app.add_middleware(GZipMiddleware, minimum_size=1000)
486
487
# Add global routes
488
app.add_route("/", homepage, name="home")
489
app.add_route("/health", health_check, name="health")
490
491
# Static files
492
app.mount("/static", StaticFiles(directory="static"), name="static")
493
```
494
495
## Testing Applications
496
497
```python { .api }
498
from starlette.testclient import TestClient
499
500
# Test application with lifespan
501
def test_application():
502
with TestClient(app) as client:
503
# Lifespan events are executed
504
response = client.get("/")
505
assert response.status_code == 200
506
507
# Test URL generation
508
assert client.app.url_path_for("home") == "/"
509
510
# Test application state
511
assert hasattr(client.app.state, "database")
512
513
# Test application without lifespan
514
def test_routes_only():
515
client = TestClient(app)
516
response = client.get("/health")
517
assert response.status_code == 200
518
client.close() # Manual cleanup when not using context manager
519
```
520
521
The Starlette application class provides a flexible foundation for building web applications with proper lifecycle management, state sharing, and composable architecture.