Skip to main content

Extending the API

FastAPI

FastAPI is a high-performant REST API framework for Python. It's built on top of Starlette, an ASGI (Asynchronous Server Gateway Interface) implementation for Python, and it uses Pydantic for data validation. It can generate OpenAPI documentation from your code and also produces a Swagger UI that you can use to test your application. OpenAPI uses a subset of JSON Schema to describe APIs and define the validation rules of the API payloads and parameters.

To run FastAPI applications, we use the process manager uvicorn. Check out the official documentation for more details.

FastAPI

Codebase structure

The API is grouped by features.

Features

The API has a feature-based folder structure following the principles of Clean Architecture.

├── api/
│ └── src/
│ ├── common/
│ ├── entities/
│ ├── features/
│ │ ├── health_check/
│ │ ├── todo/
│ │ ├── whoami/
│ │ └── ...
│ ├── data_providers/
│ └── tests/
│ ├── unit/
│ └── integration/
└── ...
  • common contains shared code like authentication, exceptions, response decorator
  • entities contains all entities, enums, exceptions, interfaces, types and logic specific to the domain layer
  • features contains use-cases (application logic), repository interfaces, and controllers
  • data providers contains classes for accessing external resources such as databases, file systems, web services, and repository implementations
  • tests contains unit and integrations tests

Get started

  1. Create the domain model by adding entities
  2. Extend the API by adding features
    • Add a use case to handle application logic
    • Add a controller to handle API requests
      • Add an endpoint to the controller that executes the use case
  3. Add a data provider, repository interface and repository to handle communication to external services such as databases.
note

Entities and data providers can be shared between features (the entrypoints and use-cases).

Configuration

All configuration parameters are expected to be environment variables, and are defined in this file api/src/config.py.

from pydantic import Field
from pydantic_settings import BaseSettings

from authentication.models import User
from common.logger_level import LoggerLevel


class Config(BaseSettings):
# Pydantic-settings in pydantic v2 automatically fetch config settings from env-variables
ENVIRONMENT: str = "local"

# Logging
LOGGER_LEVEL: LoggerLevel = Field(default=LoggerLevel.INFO)
APPINSIGHTS_CONSTRING: str | None = None

# Database
MONGODB_USERNAME: str = "dummy"
MONGODB_PASSWORD: str = "dummy"
MONGODB_HOSTNAME: str = "db"
MONGODB_DATABASE: str = "test"
MONGODB_PORT: int = 27017

# Access control
APPLICATION_ADMIN: str = "admin"
APPLICATION_ADMIN_ROLE: str = "admin"

# Authentication
SECRET_KEY: str | None = None
AUTH_ENABLED: bool = False
JWT_SELF_SIGNING_ISSUER: str = "APPLICATION" # Which value will be used to sign self-signed JWT's
TEST_TOKEN: bool = False # This value should only be changed at runtime by test setup
OAUTH_WELL_KNOWN: str | None = None
OAUTH_TOKEN_ENDPOINT: str = ""
OAUTH_AUTH_ENDPOINT: str = ""
OAUTH_CLIENT_ID: str = ""
OAUTH_AUTH_SCOPE: str = ""
OAUTH_AUDIENCE: str = ""
MICROSOFT_AUTH_PROVIDER: str = "login.microsoftonline.com"

@property
def log_level(self) -> str:
"""Returns LOGGER_LEVEL as a (lower case) string."""
return str(self.LOGGER_LEVEL.value).lower()


config = Config()

if config.AUTH_ENABLED and not all((config.OAUTH_AUTH_ENDPOINT, config.OAUTH_TOKEN_ENDPOINT, config.OAUTH_WELL_KNOWN)):
raise ValueError("Authentication was enabled, but some auth configuration parameters are missing")

if not config.AUTH_ENABLED:
print("\n")
print("################ WARNING ################")
print("# Authentication is disabled #")
print("################ WARNING ################\n")

default_user: User = User(
user_id="nologin",
full_name="Not Authenticated",
email="nologin@example.com",
)

See configuration for a description of the different configuration options available.