Skip to content

Commit 854ff84

Browse files
committed
Library API in FastAPI: N-tier, SQLAlchemy 2.0 and Alembic, Docker, CI with the conformance suite
0 parents  commit 854ff84

49 files changed

Lines changed: 2331 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.dockerignore‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.git
2+
.github
3+
.venv
4+
venv
5+
**/__pycache__
6+
**/*.pyc
7+
.pytest_cache
8+
*.db
9+
tests
10+
Dockerfile
11+
docker-compose.yml
12+
README.md

‎.github/workflows/ci.yml‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
# CI for the Library API (FastAPI): unit tests and the Docker image first, then the
2+
# conformance suite from Library_API_Contract against SQLite and against PostgreSQL 17.
3+
4+
# name: the workflow's name on the Actions tab.
5+
name: CI
6+
7+
# on: the events that start the workflow. Every push and every pull request.
8+
on:
9+
push:
10+
pull_request:
11+
12+
# jobs: the work to do. Jobs run in parallel unless one "needs" another.
13+
jobs:
14+
build:
15+
# runs-on: the kind of machine GitHub starts for the job.
16+
runs-on: ubuntu-latest
17+
# steps: run in order. "uses" runs a published action, "run" runs shell commands.
18+
steps:
19+
- name: Check out this repository
20+
uses: actions/checkout@v4
21+
22+
- name: Set up Python
23+
uses: actions/setup-python@v5
24+
# with: the inputs the action takes.
25+
with:
26+
python-version: "3.13"
27+
28+
- name: Install the packages
29+
run: pip install -r requirements-dev.txt
30+
31+
- name: Unit tests (service layer)
32+
run: python -m pytest
33+
34+
- name: Models match the migrations
35+
# env: environment variables for this step only.
36+
env:
37+
DATABASE_URL: sqlite:///ci.db
38+
run: |
39+
alembic upgrade head
40+
alembic check
41+
42+
- name: Build the Docker image
43+
run: docker build -t library-api-fastapi .
44+
45+
conformance:
46+
# needs: wait for the build job to pass first.
47+
needs: build
48+
runs-on: ubuntu-latest
49+
50+
# strategy.matrix: run this job once for each database.
51+
strategy:
52+
fail-fast: false
53+
matrix:
54+
database: [sqlite, postgresql]
55+
56+
# services: containers that run next to the job. PostgreSQL 17 is started for
57+
# both rows of the matrix; the SQLite row simply does not use it.
58+
services:
59+
postgres:
60+
image: postgres:17
61+
env:
62+
POSTGRES_USER: library
63+
POSTGRES_PASSWORD: library
64+
POSTGRES_DB: library
65+
ports:
66+
- 5432:5432
67+
options: >-
68+
--health-cmd "pg_isready -U library -d library"
69+
--health-interval 5s --health-timeout 3s --health-retries 20
70+
71+
env:
72+
PORT: "8000"
73+
TEST_MODE: "1"
74+
# ${{ }}: an expression, worked out by GitHub before the step runs.
75+
DATABASE_URL: ${{ matrix.database == 'postgresql' && 'postgresql://library:library@localhost:5432/library' || 'sqlite:///library.db' }}
76+
77+
steps:
78+
- uses: actions/checkout@v4
79+
80+
- name: Check out the contract
81+
uses: actions/checkout@v4
82+
with:
83+
repository: devfoundry-training/Library_API_Contract
84+
path: contract
85+
86+
- uses: actions/setup-python@v5
87+
with:
88+
python-version: "3.13"
89+
90+
- name: Install the packages
91+
run: pip install -r requirements.txt
92+
93+
- name: Start the API
94+
run: |
95+
nohup python -m app > app.log 2>&1 &
96+
for _ in $(seq 1 60); do curl -fs http://localhost:8000/health && break; sleep 1; done
97+
98+
- name: Conformance suite
99+
run: python contract/conformance/run.py --base-url http://localhost:8000
100+
101+
- name: Server log
102+
# if: failure() runs this step only when an earlier step failed.
103+
if: failure()
104+
run: cat app.log

‎.gitignore‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
.pytest_cache/
5+
*.egg-info/
6+
7+
# Virtual environments
8+
.venv/
9+
venv/
10+
11+
# Local SQLite databases
12+
*.db
13+
*.db-journal
14+
15+
# Editors and OS
16+
.vscode/
17+
.idea/
18+
.DS_Store
19+
Thumbs.db
20+
21+
# Local environment files
22+
.env

‎Dockerfile‎

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Library API (FastAPI). Two stages: install the packages into a virtual environment,
2+
# then copy only that environment and the source into a clean slim image.
3+
# docker build -t library-api-fastapi .
4+
# docker compose up --build (the app with PostgreSQL 17)
5+
6+
FROM python:3.13-slim AS build
7+
ENV PIP_NO_CACHE_DIR=1 PIP_DISABLE_PIP_VERSION_CHECK=1
8+
RUN python -m venv /opt/venv
9+
ENV PATH=/opt/venv/bin:$PATH
10+
COPY requirements.txt .
11+
RUN pip install -r requirements.txt
12+
13+
FROM python:3.13-slim
14+
# HOST=0.0.0.0 so the server is reachable from outside the container.
15+
# Time zone data for Europe/London comes from the tzdata package in requirements.txt.
16+
ENV PATH=/opt/venv/bin:$PATH \
17+
PYTHONDONTWRITEBYTECODE=1 \
18+
PYTHONUNBUFFERED=1 \
19+
HOST=0.0.0.0 \
20+
PORT=8000 \
21+
TZ=Europe/London
22+
RUN useradd --create-home --uid 10001 library
23+
WORKDIR /srv/library
24+
COPY --from=build /opt/venv /opt/venv
25+
COPY alembic.ini ./
26+
COPY alembic ./alembic
27+
COPY app ./app
28+
# The default DATABASE_URL (sqlite:///library.db) writes here, so the app user owns the folder.
29+
RUN chown library:library /srv/library
30+
USER library
31+
EXPOSE 8000
32+
# Runs the migrations and loads the sample data on an empty database, then serves.
33+
CMD ["python", "-m", "app"]

0 commit comments

Comments
 (0)