FastAPI-based patient registration service with PostgreSQL persistence and email confirmation, designed for simple onboarding flows and containerized development.
Healthcare and clinic teams often need a minimal, self-hosted way to capture patient contact details and store a proof-of-identity document, without pulling in a full-blown EMR system. This service provides a focused API for registering patients and persisting their basic profile and uploaded ID document.
- Patient registration endpoint with name, email, phone, and document photo upload.
- Pydantic-based input validation for strong typing and safer inputs.
- PostgreSQL-backed storage using SQLAlchemy ORM.
- Asynchronous confirmation emails via Mailtrap (SMTP).
- Dockerized setup using
Dockerfileanddocker-compose.yml. - OpenAPI docs auto-generated by FastAPI at
/docsand/redoc.
- API layer: FastAPI app defined in
app/main.pyexposes HTTP endpoints and orchestrates validation, persistence, and email side-effects. - Domain & persistence:
app/schemas.pycontains Pydantic models for requests and responses.app/models.pycontains SQLAlchemy ORM models mapped to PostgreSQL tables.app/crud.pyimplements database operations using SQLAlchemy sessions.app/database.pyconfigures the SQLAlchemy engine and session factory.
- Email integration:
app/email_utils.pywrapsfastapi-mailto send registration confirmation emails asynchronously through Mailtrap. - Infrastructure:
Dockerfilebuilds the FastAPI service image.docker-compose.ymlruns PostgreSQL and the API together in a local stack.requirements.txtdefines Python dependencies.
On startup, app/main.py initializes the FastAPI app, creates DB tables via SQLAlchemy metadata, and sets up exception handlers.
- Language: Python 3.11
- Web framework: FastAPI
- ORM: SQLAlchemy
- Validation: Pydantic
- Database: PostgreSQL
- Email: fastapi-mail + Mailtrap SMTP
- Containerization: Docker & Docker Compose
- Testing: pytest,
fastapi.testclient
.
├── app/
│ ├── __init__.py
│ ├── main.py # FastAPI application entrypoint (uvicorn app.main:app)
│ ├── models.py # SQLAlchemy ORM models (Patient)
│ ├── schemas.py # Pydantic models for request/response
│ ├── crud.py # Database operations helper functions
│ ├── database.py # SQLAlchemy engine and SessionLocal configuration
│ ├── email_utils.py # Mailtrap-based async email sender
│ └── test_app.py # API tests using pytest + TestClient
├── Dockerfile # Container image for the FastAPI app
├── docker-compose.yml # Local dev stack: Postgres + API
├── requirements.txt # Python dependencies
├── test_main.http # HTTP requests collection for manual testing
├── .gitignore
├── .editorconfig
└── .env.example # Example environment configuration (not used directly in code)
Note:
app/main.pyis the production entrypoint used by Docker. The rootmain.pyfile is a simple example app and is not used in the container setup.
git clone <your-fork-or-repo-url>
cd patient_registerIf you want to run the app locally without Docker:
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -r requirements.txtCopy .env.example to .env and fill in your local values:
cp .env.example .envRequired values:
DATABASE_URL— connection string for PostgreSQL.- Docker defaults to
postgresql://postgres:postgres@db:5432/patients_db. - For local, you can use
postgresql://postgres:postgres@localhost:5432/patients_db.
- Docker defaults to
MAILTRAP_USER,MAILTRAP_PASS— your Mailtrap SMTP credentials.
These values are read by app/database.py and app/email_utils.py via os.getenv.
Ensure Docker is running, then from the project root:
docker-compose up --buildThis will:
- Start a PostgreSQL container (
dbservice). - Build and start the FastAPI app container (
webservice) running:uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
Once running, you can access:
- Health check:
http://localhost:8000/ - Interactive API docs:
http://localhost:8000/docs - Redoc docs:
http://localhost:8000/redoc
You need a running PostgreSQL instance and the DATABASE_URL environment variable pointing to it.
export DATABASE_URL=postgresql://postgres:postgres@localhost:5432/patients_db
uvicorn app.main:app --reloadOn Windows PowerShell:
$env:DATABASE_URL = "postgresql://postgres:postgres@localhost:5432/patients_db"
uvicorn app.main:app --reloadThe app will be available at http://127.0.0.1:8000/.
Health check endpoint.
- Response:
200 OK—{"message": "Patient Registration API"}
Example:
curl http://localhost:8000/Create a new patient with a document photo.
-
Request:
multipart/form-datawith fields:name(string, required)email(string, valid email, required, unique)phone(string, 7–20 chars, required)document_photo(file, JPEG or PNG, max 5MB, required)
-
Validation:
- Pydantic validation on name, email, phone.
- Rejects if email is already registered.
- Rejects if content type is not image.
- Rejects if size exceeds 5MB.
- Rejects if image is not detected as JPEG or PNG.
-
Response (
200 OK):
{
"id": 1,
"name": "John Doe",
"email": "[email protected]",
"phone": "1234567890",
"document_photo": "app/uploads/john.doe_at_example.com.png"
}- Example cURL:
curl --location 'http://localhost:8000/patients' \
--form 'name="John Doe"' \
--form 'email="[email protected]"' \
--form 'phone="1234567890"' \
--form 'document_photo=@"/path/to/document.png"'Uploaded files are stored under app/uploads/ in the running service and their filesystem path is persisted in the database.
To run the automated tests locally (assuming dependencies are installed and a DB is available as per DATABASE_URL):
pytestThe test suite in app/test_app.py:
- Verifies the health check root endpoint.
- Exercises the
POST /patientsendpoint end-to-end, including file upload logic.
Note: Tests currently assume a working PostgreSQL database. For isolated runs, it’s recommended to point
DATABASE_URLto a disposable database (e.g., thedbservice indocker-compose.yml).
You can also use test_main.http with an HTTP client supporting .http files (e.g., VS Code REST Client) to manually hit the API endpoints from your editor.
-
Run services with Docker Compose:
docker-compose up --build
-
Run app locally with Uvicorn:
uvicorn app.main:app --reload
-
Run tests:
pytest
You can easily wrap these in make targets or shell scripts if desired.
- Container-based deployment:
- Build the image using the provided
Dockerfile. - Configure environment variables (
DATABASE_URL,MAILTRAP_USER,MAILTRAP_PASS) via your orchestrator (Kubernetes, ECS, etc.). - Expose port
8000(or configure your own).
- Build the image using the provided
- Database migrations:
- Currently, the app uses
models.Base.metadata.create_all(...)on startup. - For production environments with evolving schemas, you should introduce Alembic migrations to manage schema changes in a controlled way.
- Currently, the app uses
- Secrets management:
- Do not commit real secrets.
- Use your platform’s secret store (e.g., AWS Secrets Manager, Vault, Kubernetes Secrets) for Mailtrap and DB credentials.
Contributions are welcome. To keep the project maintainable and predictable:
- Fork the repository and create a feature branch:
git checkout -b feature/my-improvement
- Set up your environment (see “Installation & environment setup”).
- Add or update tests in
app/test_app.pyfor your changes. - Run tests locally:
pytest
- Open a Pull Request:
- Describe the motivation and behavior change.
- Link to any related issues.
- Include screenshots or logs if relevant.
Please keep changes focused and small where possible, and avoid breaking the public API without clear justification.
Specify your chosen license here (for example, MIT, Apache 2.0, or proprietary). If you intend this project to be open source, add a LICENSE file at the root of the repository and reference it from this section.