Pastaba.
Prieigai prie šio puslapio reikalingas įgaliojimas. Galite bandyti prisijungti arba pakeisti katalogus.
Prieigai prie šio puslapio reikalingas įgaliojimas. Galite bandyti pakeisti katalogus.
After you build a FastAPI application with mssql-python, configure it for deployment, connection reuse, error handling, authentication, and automated testing.
Prerequisites
Complete Use mssql-python with FastAPI, or have an equivalent FastAPI application that uses the AdventureWorksLT sample database. The authentication dependency in this article queries
SalesLT.Customer.Install the production and test dependencies:
pip install pydantic-settings pyjwt pytest httpx
Configure deployment settings
Use Pydantic Settings to load deployment-specific values from environment variables. This approach keeps secrets out of source code and gives each environment its own database, pool, and authentication configuration.
Create config.py:
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_server: str
database_name: str
pool_size: int = 20
pool_idle_timeout: int = 300
jwt_secret: str
settings = Settings()
def get_connection_string() -> str:
return (
f"Server={settings.database_server};"
f"Database={settings.database_name};"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes"
)
Set DATABASE_SERVER, DATABASE_NAME, and JWT_SECRET in the deployment environment. Pydantic Settings reads the uppercase environment variable names automatically.
Note
ActiveDirectoryDefault tries multiple credential providers in sequence. In production, specify the authentication mode for the deployed identity, such as ActiveDirectoryMSI for managed identity, to avoid walking the credential chain. For available modes, see Microsoft Entra authentication with mssql-python.
Configure connection pooling
mssql-python enables connection pooling by default. Configure the pool once, before the application creates its first connection. Size the pool for the application's expected concurrent database work and the database service tier.
Update database.py to use the deployment settings:
from collections.abc import Generator
import mssql_python
from config import get_connection_string, settings
mssql_python.pooling(
max_size=settings.pool_size,
idle_timeout=settings.pool_idle_timeout,
)
def get_db_dependency() -> Generator:
with mssql_python.connect(get_connection_string()) as conn:
with conn.cursor() as cursor:
yield cursor
The connection context manager commits after successful request processing, rolls back when request processing raises an exception, and closes the connection. Closing the connection returns it to the pool. For pool keys, sizing, identity isolation, and exhaustion guidance, see Connection pooling with mssql-python.
Handle database errors
Register exception handlers so database failures return consistent responses without exposing connection details, queries, or server error text.
Add the handlers after app = FastAPI(...) in main.py:
import mssql_python
from fastapi import Request
from fastapi.responses import JSONResponse
@app.exception_handler(mssql_python.IntegrityError)
async def integrity_exception_handler(
request: Request,
exc: mssql_python.IntegrityError,
):
return JSONResponse(
status_code=409,
content={
"detail": "The request conflicts with existing data.",
"type": "integrity_error",
},
)
@app.exception_handler(mssql_python.DatabaseError)
async def database_exception_handler(
request: Request,
exc: mssql_python.DatabaseError,
):
return JSONResponse(
status_code=500,
content={
"detail": "A database operation failed.",
"type": "database_error",
},
)
Log the exception through your application's protected telemetry pipeline before returning the response. For the exception hierarchy and SQLSTATE handling, see Error handling and SQLSTATE codes for mssql-python.
Add authentication dependencies
Chain FastAPI dependencies to validate a JSON Web Token (JWT), load the matching AdventureWorksLT customer, and make that customer available to protected routes. Validate the token before acquiring a database connection so an invalid token doesn't use a pooled connection.
Create auth.py:
import jwt
from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from config import settings
from database import get_db_dependency
security = HTTPBearer()
def get_customer_id(
credentials: HTTPAuthorizationCredentials = Depends(security),
) -> int:
try:
payload = jwt.decode(
credentials.credentials,
settings.jwt_secret,
algorithms=["HS256"],
)
customer_id = int(payload["sub"])
except (KeyError, TypeError, ValueError):
raise HTTPException(status_code=401, detail="Invalid token subject")
except jwt.ExpiredSignatureError:
raise HTTPException(status_code=401, detail="Token expired")
except jwt.InvalidTokenError:
raise HTTPException(status_code=401, detail="Invalid token")
return customer_id
def get_current_customer(
customer_id: int = Depends(get_customer_id),
cursor = Depends(get_db_dependency),
):
cursor.execute(
"""
SELECT CustomerID, FirstName, LastName
FROM SalesLT.Customer
WHERE CustomerID = %(id)s
""",
{"id": customer_id},
)
customer = cursor.fetchone()
if customer is None:
raise HTTPException(status_code=401, detail="Customer not found")
return {
"id": customer.CustomerID,
"first_name": customer.FirstName,
"last_name": customer.LastName,
}
Import the dependency and add a protected route to main.py:
from auth import get_current_customer
@app.get("/me")
def get_me(current_customer: dict = Depends(get_current_customer)):
return current_customer
Use an identity provider to issue and rotate signing keys. For HS256, set JWT_SECRET to at least 32 random bytes. Don't store a production signing secret in the repository or in an image.
Test the application
FastAPI's TestClient sends requests to the application without starting an HTTP server. The following integration tests use the configured database.
Create test_api.py:
import uuid
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_list_products():
response = client.get("/products")
assert response.status_code == 200
data = response.json()
assert "items" in data
assert "total" in data
def test_create_product():
suffix = uuid.uuid4().hex[:8]
response = client.post(
"/products",
json={
"name": f"Test Product {suffix}",
"product_number": f"TEST-{suffix}",
"price": 19.99,
"color": "Red",
"size": "M",
"category_id": 1,
},
)
assert response.status_code == 201
data = response.json()
assert data["product_number"] == f"TEST-{suffix}"
assert data["price"] == 19.99
def test_get_product_not_found():
response = client.get("/products/99999")
assert response.status_code == 404
def test_health_check():
response = client.get("/health")
assert response.status_code == 200
assert response.json()["status"] == "healthy"
Run the tests from the project root:
pytest
These tests use the configured database, and test_create_product inserts a row into SalesLT.Product. Use a dedicated test database and reset its data between test runs.
Deployment checklist
- Set
DATABASE_SERVER,DATABASE_NAME, andJWT_SECRETthrough the deployment platform's secret and configuration stores. - Use a dedicated Microsoft Entra identity with the minimum required database permissions.
- Set pool size below the database's connection limit and leave capacity for administrative access and other workloads.
- Run database integration tests against an isolated test database.
- Configure protected telemetry for database exceptions, request latency, and pool exhaustion.
- Run Uvicorn without
--reloadin deployed environments.