Mastering Starlette: A Lightweight ASGI Framework for High-Performance Web Applications
Starlette is a lightweight and fast ASGI framework for building high-performance web applications in Python. Its simplicity and efficiency make it an ideal choice for developers looking to create scalable APIs, real-time services, and microservices. Starlette comes with a rich set of features, including request and response handling, routing, middleware, WebSocket support, and more. Let’s dive into its core concepts and explore some useful APIs through examples.
Understanding Starlette’s Features
Here are some of the key features of Starlette:
- ASGI-based for asynchronous programming.
- Support for HTTP and WebSocket protocols.
- Fast routing with path parameters.
- Dependency Injection with
Depends
. - Middleware for request/response manipulation.
- Automatic schema generation with OpenAPI and JSON Schema.
- File serving and request parsing support.
Starlette APIs with Examples
Below are some of the most useful APIs that Starlette offers, along with code snippets to help you understand their usage:
Basic Application
from starlette.applications import Starlette from starlette.responses import PlainTextResponse app = Starlette(debug=True) @app.route("/") async def homepage(request): return PlainTextResponse("Hello, Starlette!")
This creates a simple application with a single route that returns a plain text response.
Path Parameters
@app.route("/greet/{name}") async def greet(request): name = request.path_params["name"] return PlainTextResponse(f"Hello, {name}!")
Access dynamic segments of the URL via path_params
.
Query Parameters
@app.route("/search") async def search(request): query = request.query_params.get("query", "default") return PlainTextResponse(f"Search query: {query}")
Extract query parameters using query_params
.
Middleware
from starlette.middleware.base import BaseHTTPMiddleware class CustomMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): response = await call_next(request) response.headers["X-Custom-Header"] = "CustomValue" return response app.add_middleware(CustomMiddleware)
Use middleware to modify requests or responses during the application lifecycle.
WebSocket Support
@app.websocket_route("/ws") async def websocket_endpoint(websocket): await websocket.accept() data = await websocket.receive_text() await websocket.send_text(f"Message received: {data}") await websocket.close()
Handle WebSocket connections for real-time features.
Static File Handling
from starlette.staticfiles import StaticFiles app.mount("/static", StaticFiles(directory="static"), name="static")
This serves files from the static/
directory at the /static
endpoint.
Dependency Injection
from starlette.requests import Request from starlette.responses import JSONResponse from starlette.dependencies import Depends async def query_user(request: Request): return {"user": "Jane Doe"} @app.route("/user") async def user_info(info=Depends(query_user)): return JSONResponse(info)
Leverage Depends
for better modularity and testability.
Using Background Tasks
from starlette.background import BackgroundTasks async def log_message(message: str): with open("log.txt", "a") as log_file: log_file.write(message + "\n") @app.route("/notify") async def send_notification(background_tasks: BackgroundTasks): background_tasks.add_task(log_message, "New notification triggered!") return PlainTextResponse("Notification sent.")
Perform background tasks without delaying the response.
Complete Application Example
Here is a complete example that ties together all the concepts we have discussed above:
from starlette.applications import Starlette from starlette.responses import JSONResponse, PlainTextResponse from starlette.routing import Route, WebSocketRoute from starlette.staticfiles import StaticFiles from starlette.background import BackgroundTasks from starlette.middleware.base import BaseHTTPMiddleware async def homepage(request): return PlainTextResponse("Welcome to Starlette!") async def greet(request): name = request.path_params.get("name", "User") return PlainTextResponse(f"Hello, {name}!") async def log_message(message: str): with open("log.txt", "a") as log_file: log_file.write(message + "\n") async def send_notification(background_tasks: BackgroundTasks): background_tasks.add_task(log_message, "Notification triggered!") return PlainTextResponse("Notification sent and logged.") async def websocket_endpoint(websocket): await websocket.accept() data = await websocket.receive_text() await websocket.send_text(f"Received: {data}") await websocket.close() class CustomMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): response = await call_next(request) response.headers["X-Custom-Header"] = "Middleware Applied" return response routes = [ Route("/", endpoint=homepage), Route("/greet/{name}", endpoint=greet), Route("/notify", endpoint=send_notification), WebSocketRoute("/ws", endpoint=websocket_endpoint), ] app = Starlette(debug=True, routes=routes) app.add_middleware(CustomMiddleware) app.mount("/static", StaticFiles(directory="static"), name="static")
This example demonstrates how to use Starlette’s routing, middleware, WebSocket support, background tasks, and static file serving in a cohesive application.
Conclusion
Starlette is a powerful and versatile framework for building modern web applications and APIs. Its lightweight design and extensive feature set make it a great choice for projects of any scale. Whether you’re creating a simple API or a complex service, Starlette has you covered.