Nobody has started this yet — be first.
Business impact
Today there is no way to ask the API "what images exist?" -- a client (or an internal admin tool, or a support engineer trying to help a confused user) can only look up a single image if it already knows the id. Any dashboard, gallery UI, or "show me my uploads" feature that a product team wants to build on top of this service is blocked until this endpoint exists. This is the single most common gap reported by teams integrating with a single-resource-lookup-only API.
Problem
GET /images exists as a stub only. It accepts limit/offset query parameters and returns the right shape (ImageListResponse), but the handler ignores the database entirely and always returns {"items": [], "total": 0, "limit": ..., "offset": ...}, no matter how many images have actually been uploaded. This is enough for the route to exist, be documented in /docs, and compile -- but it is not a real implementation.
Current behavior
GET /images?limit=&offset= always responds with an empty items array and total: 0, regardless of how many images are actually stored in the database.
Expected behavior
GET /images?limit=&offset= should return the images actually stored in the database, most-recently-created first, with real pagination: items holds up to limit images starting after offset items, ordered by created_at descending; total reflects the full matching count in the database, not just the page size, so a client can compute how many pages exist; limit and offset are echoed back as received (already correct in the stub). Each item should carry the same summary information GET /images/{id} exposes for a single image -- id, status, original_url, format, width, height, file_size, created_at -- via the already-defined ImageSummary schema.
Steps to reproduce
uvicorn app.main:app --reload
curl -s "http://localhost:8000/images?limit=1&offset=1"
Why this matters
models.ImageRecord (app/models.py) already has everything needed: created_at for ordering, and all the summary fields. This is a pure database-querying exercise using the db: Session already injected into the route via Depends(get_db) -- no new storage or processing logic is required.
Suggested approach
Look at list_images in app/main.py -- it's the GET /images route, currently a two-line stub. You'll want to query db.query(models.ImageRecord) (or the SQLAlchemy 2.0 select() style, if you prefer -- get_image_metadata right below shows the db.get(...) style used elsewhere in this file, though a query for all rows needs something different from a single-row get), order by created_at descending, get a total count of matching rows before applying limit/offset, then apply .offset(offset).limit(limit) for the page itself. Build ImageSummary objects from each row the same way get_image_metadata builds an ImageMetadataResponse from one.
Acceptance criteria
Verification
pytest practicetickets/test_ticket03_list_images_pagination.py -v
Hints (0/2)
Try it without hints first — the reading is the exercise.
Working on this ticket
Work on a branch named for the ticket — that's what you'll submit.
Branch off your fork
$git checkout -b feat/list-images-paginationFix it and commit
Meet every acceptance criterion, and add a test that would have caught this.
Push the branch
$git push -u origin feat/list-images-paginationSubmit it below
Paste your fork URL and the branch name, with a short write-up of the root cause.
Questions
Ask about anything unclear in the ticket — the maintainer and anyone who has solved it can answer. Please don't post full solutions.
Sign in to ask a question or reply.
Sign indocker-compose.yml./app./testsuvicorn --reload/app/dataDockerfile.proddocker compose -f docker-compose.prod.yml up --build -dWithout Docker:
cd fastapi/image_service
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env # optional, defaults work out of the box
uvicorn app.main:app --reload # -> http://127.0.0.1:8000/docs
Work the tickets in practicetickets/ (TICKET_01 through TICKET_07); each names one dedicated pytest file:
pytest practicetickets/test_ticket01_upload_size_boundary.py -v # a single ticket
./practicetickets/run_tickets.sh # all 7, clean pass/fail summary
Run run_tickets.sh from anywhere -- it cds to the project root itself before running, since app/main.py resolves its SQLite file and upload directory relative to the working directory at import time. pytest -q tests/ (the project's own pre-existing suite, separate from practicetickets/) is also worth running before and after each fix -- three of the four bugs (tickets 02, 04, 06) also break specific pre-existing tests there, and fixing the ticket correctly should make those green again too, with zero changes needed inside tests/ itself.
Level 2
Implement a feature
Extend the system within its own patterns.