Initial commit
Some checks failed
Run linters on applied template / Python 3.13 lint and build (push) Failing after 41s
Some checks failed
Run linters on applied template / Python 3.13 lint and build (push) Failing after 41s
This is a FastAPI backend microservice template used with `copier` utility. Features of applied template are: - Configuration file processing logic - Metrics and tracing (both optional) configuration available - Debug endpoints - Database migration commands, prepared Alembic environment - Database usage example in ping_db endpoint - gitea sanity check pipeline
This commit is contained in:
150
{{project_slug}}/fastapi_init.py.jinja
Normal file
150
{{project_slug}}/fastapi_init.py.jinja
Normal file
@@ -0,0 +1,150 @@
|
||||
"""FastAPI application initialization is performed here."""
|
||||
|
||||
import os
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from fastapi.openapi.docs import get_swagger_ui_html
|
||||
from fastapi.responses import JSONResponse
|
||||
|
||||
from {{project_slug}}.config import {{ProjectName}}Config
|
||||
from {{project_slug}}.db.connection.manager import PostgresConnectionManager
|
||||
from {{project_slug}}.dependencies import connection_manager_dep, logger_dep
|
||||
from {{project_slug}}.exceptions.mapper import ExceptionMapper
|
||||
from {{project_slug}}.handlers.debug import DebugException, DebugExceptionWithParams
|
||||
from {{project_slug}}.middlewares.exception_handler import ExceptionHandlerMiddleware
|
||||
from {{project_slug}}.middlewares.observability import ObservabilityMiddleware
|
||||
from {{project_slug}}.observability.metrics import init_metrics
|
||||
from {{project_slug}}.observability.otel_agent import OpenTelemetryAgent
|
||||
from {{project_slug}}.utils.observability import URLsMapper, configure_logging
|
||||
|
||||
from .handlers import list_of_routers
|
||||
from .version import LAST_UPDATE, VERSION
|
||||
|
||||
|
||||
def _get_exception_mapper(debug: bool) -> ExceptionMapper:
|
||||
mapper = ExceptionMapper()
|
||||
if debug:
|
||||
mapper.register_simple(DebugException, 506, "That's how a debug exception look like")
|
||||
mapper.register_func(
|
||||
DebugExceptionWithParams,
|
||||
lambda exc: JSONResponse(
|
||||
{"error": "That's how a debug exception with params look like", "message": exc.message},
|
||||
status_code=exc.status_code,
|
||||
),
|
||||
)
|
||||
return mapper
|
||||
|
||||
|
||||
def bind_routes(application: FastAPI, prefix: str, debug: bool) -> None:
|
||||
"""Bind all routes to application."""
|
||||
for router in list_of_routers:
|
||||
if not debug:
|
||||
to_remove = []
|
||||
for i, route in enumerate(router.routes):
|
||||
if "debug" in route.path:
|
||||
to_remove.append(i)
|
||||
for i in to_remove[::-1]:
|
||||
del router.routes[i]
|
||||
if len(router.routes) > 0:
|
||||
application.include_router(router, prefix=(prefix if "/" not in {r.path for r in router.routes} else ""))
|
||||
|
||||
|
||||
def get_app(prefix: str = "/api") -> FastAPI:
|
||||
"""Create application and all dependable objects."""
|
||||
|
||||
if "CONFIG_PATH" not in os.environ:
|
||||
raise ValueError("CONFIG_PATH environment variable is not set")
|
||||
app_config: {{ProjectName}}Config = {{ProjectName}}Config.from_file(os.getenv("CONFIG_PATH"))
|
||||
|
||||
description = "{{project_description}}"
|
||||
|
||||
application = FastAPI(
|
||||
title="{{project_name}}",
|
||||
description=description,
|
||||
docs_url=None,
|
||||
openapi_url=f"{prefix}/openapi",
|
||||
version=f"{VERSION} ({LAST_UPDATE})",
|
||||
terms_of_service="http://swagger.io/terms/",
|
||||
contact={"email": "idu@itmo.ru"},
|
||||
license_info={"name": "Apache 2.0", "url": "http://www.apache.org/licenses/LICENSE-2.0.html"},
|
||||
lifespan=lifespan,
|
||||
)
|
||||
bind_routes(application, prefix, app_config.app.debug)
|
||||
|
||||
@application.get(f"{prefix}/docs", include_in_schema=False)
|
||||
async def custom_swagger_ui_html():
|
||||
return get_swagger_ui_html(
|
||||
openapi_url=application.openapi_url,
|
||||
title=application.title + " - Swagger UI",
|
||||
oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
|
||||
swagger_js_url="https://unpkg.com/swagger-ui-dist@5.11.7/swagger-ui-bundle.js",
|
||||
swagger_css_url="https://unpkg.com/swagger-ui-dist@5.11.7/swagger-ui.css",
|
||||
)
|
||||
|
||||
application.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=app_config.app.cors.allow_origins,
|
||||
allow_credentials=app_config.app.cors.allow_credentials,
|
||||
allow_methods=app_config.app.cors.allow_methods,
|
||||
allow_headers=app_config.app.cors.allow_headers,
|
||||
)
|
||||
|
||||
application.state.config = app_config
|
||||
|
||||
exception_mapper = _get_exception_mapper(app_config.app.debug)
|
||||
metrics = init_metrics()
|
||||
urls_mapper = URLsMapper(app_config.observability.prometheus.urls_mapping)
|
||||
|
||||
application.add_middleware(
|
||||
ObservabilityMiddleware,
|
||||
exception_mapper=exception_mapper,
|
||||
metrics=metrics,
|
||||
urls_mapper=urls_mapper,
|
||||
)
|
||||
application.add_middleware(
|
||||
ExceptionHandlerMiddleware,
|
||||
debug=[False], # reinitialized on startup
|
||||
exception_mapper=exception_mapper,
|
||||
)
|
||||
|
||||
return application
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(application: FastAPI):
|
||||
"""Lifespan function.
|
||||
|
||||
Initializes database connection in pass_services_dependencies middleware.
|
||||
"""
|
||||
app_config: {{ProjectName}}Config = application.state.config
|
||||
loggers_dict = {logger_config.filename: logger_config.level for logger_config in app_config.logging.files}
|
||||
logger = configure_logging(app_config.logging.level, loggers_dict)
|
||||
|
||||
await logger.ainfo("application is being configured", config=app_config.to_order_dict())
|
||||
|
||||
connection_manager = PostgresConnectionManager(
|
||||
master=app_config.db.master,
|
||||
replicas=app_config.db.replicas,
|
||||
logger=logger,
|
||||
application_name=f"{{project_slug}}_{VERSION}",
|
||||
)
|
||||
connection_manager_dep.init_dispencer(application, connection_manager)
|
||||
logger_dep.init_dispencer(application, logger)
|
||||
|
||||
for middleware in application.user_middleware:
|
||||
if middleware.cls == ExceptionHandlerMiddleware:
|
||||
middleware.kwargs["debug"][0] = app_config.app.debug
|
||||
|
||||
otel_agent = OpenTelemetryAgent(
|
||||
app_config.observability.prometheus,
|
||||
app_config.observability.jaeger,
|
||||
)
|
||||
|
||||
yield
|
||||
|
||||
otel_agent.shutdown()
|
||||
|
||||
|
||||
app = get_app()
|
||||
Reference in New Issue
Block a user