laranevans.com
Guides / Python and FastAPI for Spring Boot Engineers / Replace Spring Boot's Conveniences

Spring Boot hands you an IoC container, externalized configuration, bean validation, and Spring Data repositories, mostly through annotations and auto-configuration. Python gives you each capability through a focused library. This page names the stand-in for each one. The next two pages put them to work in a FastAPI service.

Dependency injection

Spring's container scans for beans and wires them through @Autowired. Python has no container, and most code does not need one. You construct objects and pass them in. For request-scoped wiring in a web app, FastAPI provides Depends, which calls a provider function and injects the result into your handler:

from typing import Annotated
from fastapi import Depends

def get_user_service() -> UserService:
    return UserService()

UserServiceDep = Annotated[UserService, Depends(get_user_service)]

A handler that declares service: UserServiceDep receives a UserService. The provider replaces @Bean and @Autowired. Page 5 uses this pattern in a route.

Spring Boot Python
@Autowired, constructor injection pass the object in, or Depends in a web handler
@Bean a provider function
@Component, @Service a plain class
IoC container no container, explicit construction

Configuration

Spring Boot reads application.yml and binds it to @ConfigurationProperties classes, with profiles per environment. The Python counterpart is pydantic-settings (uv add pydantic-settings), which reads environment variables and a .env file into a typed, validated settings object:

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env")

    database_url: str
    request_timeout: int = 30

settings = Settings()

A missing database_url fails at startup with a clear error, the way a missing required property stops a Spring context from loading. @Value("${request.timeout}") becomes a typed field with a default.

Validation

Spring uses Bean Validation (@Valid, @NotNull, @Size) on request DTOs. Python uses pydantic, where the type annotations are the validation and Field adds constraints:

from pydantic import BaseModel, EmailStr, Field

class CreateUser(BaseModel):
    email: EmailStr
    display_name: str = Field(min_length=1, max_length=80)
    age: int = Field(ge=0)

Construct CreateUser(**payload) and pydantic coerces and validates, raising ValidationError on bad input. EmailStr needs the validator extra (uv add "pydantic[email]"). FastAPI runs this validation for you on every request body, covered on page 5.

Bean Validation pydantic
@NotNull a non-optional field
@Size(min=1, max=80) Field(min_length=1, max_length=80)
@Min(0) Field(ge=0)
@Email EmailStr
@Valid on a controller argument automatic on a typed FastAPI body

Persistence

Spring Data JPA gives you @Entity classes and repository interfaces it implements at runtime. The Python counterpart is SQLAlchemy 2.0. You declare mapped classes the way you annotate entities:

from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(unique=True)
    display_name: Mapped[str] = mapped_column()
    active: Mapped[bool] = mapped_column(default=True)

One difference matters. Spring Data generates repository implementations from interface method names like findByEmail. SQLAlchemy gives you a Session and you write the query, or wrap it in a small repository class of your own:

from sqlalchemy import select

def find_by_email(session, email: str) -> User | None:
    return session.scalars(select(User).where(User.email == email)).first()
Spring Data JPA SQLAlchemy 2.0
@Entity a Base subclass with Mapped columns
@Id @GeneratedValue mapped_column(primary_key=True)
JpaRepository<User, Long> a Session plus select(...), or your own repo class
findByEmail(...) session.scalars(select(User).where(...))
spring.jpa.hibernate.ddl-auto Base.metadata.create_all(engine)

Page 6 wires a session into the request lifecycle and runs a query end to end.

Next: build a REST API with FastAPI.