Skip to content

18 update dependencies #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 17 additions & 18 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ on:
- main

env: # environment variables (available in any part of the action)
NODE_VERSION: 21
NODE_VERSION: 22
PYTHON_VERSION: 3.12
MONGODB_VERSION: 8.0

Expand Down Expand Up @@ -53,10 +53,19 @@ jobs:
- name: Check out Git repository
uses: actions/checkout@v4

- name: Run black
uses: psf/black@stable
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
options: "--config backend/pyproject.toml"
enable-cache: true

- name: Run black
run: uv run --group dev black --check --config ./pyproject.toml .

- name: Run mypy
run: uv run --group dev mypy --config-file=pyproject.toml

- name: Run ruff
run: uv run --group dev ruff check

test-backend:
name: Run backend unit tests
Expand All @@ -70,28 +79,18 @@ jobs:
- name: Git checkout
uses: actions/checkout@v4

- name: Install poetry
run: pipx install poetry

- name: Set up Python
uses: actions/setup-python@v5
- name: Install uv
uses: astral-sh/setup-uv@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: "poetry"
enable-cache: true

- name: Start MongoDB
uses: supercharge/[email protected]
with:
mongodb-version: ${{ env.MONGODB_VERSION }}

# Install dependencies. `--no-root` means "install all dependencies but not the project
# itself", which is what you want to avoid caching _your_ code. The `if` statement
# ensures this only runs on a cache miss.
- name: Install dependencies
run: poetry install --no-interaction --no-root --with dev

- name: Test with pytest
run: poetry run pytest --cov=app tests --cov-report html
run: uv run --group dev pytest --cov=app tests --cov-report html

# upload-artifact action does not take into account working-directory default
# see https://github.com/actions/upload-artifact/issues/232
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
.vscode
.mypy_cache
.pytest_cache
.ruff_cache
27 changes: 12 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,36 @@ default_language_version:
python: python3.12

repos:
# general checks (see here: https://pre-commit.com/hooks.html
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-toml
- id: check-yaml
args: [--allow-multiple-documents]
- id: end-of-file-fixer
- id: trailing-whitespace

# black - formatting
- repo: https://github.com/psf/black
rev: 24.8.0
rev: 25.1.0
hooks:
- id: black
name: black
args:
- "--config"
- "./backend/pyproject.toml"
args: [--config=./backend/pyproject.toml]

# ruff - linting
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.9
rev: "v0.11.10"
hooks:
# Run the linter.
- id: ruff
# Run the formatter.
- id: ruff-format
name: ruff
args: [--config=./backend/pyproject.toml]

# mypy - lint-like type checking
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
rev: v1.15.0
hooks:
- id: mypy
name: mypy
args:
- "--config"
- "./backend/pyproject.toml"
- "--ignore-missing-imports"
additional_dependencies: [types-requests==2.32.0.20250515]
args: [--config-file=./backend/pyproject.toml, --ignore-missing-imports]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
![Tests](https://github.com/jonasrenault/fastapi-react-mongodb-docker/actions/workflows/test.yml/badge.svg)
![Build](https://github.com/jonasrenault/fastapi-react-mongodb-docker/actions/workflows/build.yml/badge.svg)
[![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
![python_version](https://img.shields.io/badge/Python-%3E=3.10-blue)
![python_version](https://img.shields.io/badge/Python-%3E=3.12-blue)

This is a template application for a FARM stack. FARM stands for FastAPI, React, MongoDB.

Expand Down
37 changes: 29 additions & 8 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
FROM python:3.12-slim
FROM python:3.12-slim-bookworm

ENV PYTHONUNBUFFERED=true

WORKDIR /app

### install poetry
RUN pip install poetry && poetry config virtualenvs.in-project true
# Install uv
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
COPY --from=ghcr.io/astral-sh/uv:0.5.11 /uv /uvx /bin/

### install dependencies and project
# Place executables in the environment at the front of the path
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#using-the-environment
ENV PATH="/app/.venv/bin:$PATH"

# Compile bytecode
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#compiling-bytecode
ENV UV_COMPILE_BYTECODE=1

# uv Cache
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#caching
ENV UV_LINK_MODE=copy

# Install dependencies
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project

# Copy the project into the image
ADD pyproject.toml README.md ./
ADD app /app/app
RUN poetry install --no-interaction --no-ansi

### add executables to path
ENV PATH="/app/.venv/bin:$PATH"
# Sync the project
# Ref: https://docs.astral.sh/uv/guides/integration/docker/#intermediate-layers
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync

### default cmd: run fastapi with 4 workers
# default cmd: run fastapi with 4 workers
CMD ["fastapi", "run", "--workers", "4", "app/main.py"]
14 changes: 5 additions & 9 deletions backend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,22 @@ This directory contains the backend API app. It is built with [FastAPI](https://

## Requirements

* [Poetry](https://python-poetry.org/) for Python package and environment management.
* [uv](https://docs.astral.sh/uv/) for Python package and environment management.
* A [MongoDB](https://www.mongodb.com/) database for persistence.

## Install

The project uses poetry to manage dependencies and run the backend application. You can use an other tool to manage your virtual environment, such as `pip` or [uv](https://docs.astral.sh/uv/), but you'll need to extract the dependencies from the [pyproject.toml](./pyproject.toml) file.

If using poetry, for convenience, run `poetry config virtualenvs.in-project true` before installing depencies. This will install the dependencies in the project directory and make it easier to manage in vscode.

You can then install the dependencies with `poetry install --with dev`.
The project uses [uv](https://docs.astral.sh/uv/) to manage dependencies and run the backend application. You can install the project and its dependencies with `uv sync`.

## Running the server

To start a development server, run

```console
poetry run fastapi dev app/main.py
uv run fastapi dev app/main.py
```

from the `backend` directory (remove the `poetry run` prefix if using another dependency management tool).
from the `backend` directory (remove the `uv run` prefix if using another dependency management tool).

This will start a server running on `http://127.0.0.1:8000`. The API will be available on the API's base prefix, which by default is `/api/v1`.

Expand All @@ -36,7 +32,7 @@ Navigate to `http://localhost:8000/redoc` to access the API's alternative doc bu
Run

```console
poetry run pytest
uv run pytest
```

to run the unit tests for the backend app.
Expand Down
12 changes: 7 additions & 5 deletions backend/app/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,14 @@ def verify_password(password: str, hashed_pass: str) -> bool:
return password_context.verify(password, hashed_pass)


async def authenticate_user(email: str, password: str):
async def authenticate_user(email: str, password: str) -> models.User | None:
user = await models.User.find_one({"email": email})
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return None
if user.hashed_password is None or not verify_password(
password, user.hashed_password
):
return None
return user


Expand Down Expand Up @@ -112,7 +114,7 @@ async def _get_current_user(token):
)
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
userid: UUID = payload.get("sub")
userid: UUID | None = payload.get("sub")
if userid is None:
raise credentials_exception
token_data = schemas.TokenPayload(uuid=userid)
Expand Down
8 changes: 3 additions & 5 deletions backend/app/routers/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ async def login_access_token(form_data: OAuth2PasswordRequestForm = Depends()) -
OAuth2 compatible token login, get an access token for future requests
"""
user = await authenticate_user(form_data.username, form_data.password)
if not user:
if user is None:
raise HTTPException(status_code=400, detail="Incorrect email or password")
elif not user.is_active:
raise HTTPException(status_code=400, detail="Inactive user")
Expand Down Expand Up @@ -79,8 +79,7 @@ async def google_login(google_sso: GoogleSSO = Depends(get_google_sso)):
"""
Generate login url and redirect
"""
with google_sso:
return await google_sso.get_login_redirect()
return await google_sso.get_login_redirect()


@router.get("/google/callback")
Expand All @@ -97,8 +96,7 @@ async def google_callback(
)

# Get user details from Google
with google_sso:
google_user = await google_sso.verify_and_process(request)
google_user = await google_sso.verify_and_process(request)

if google_user is None:
raise HTTPException(
Expand Down
Loading