Skip to content

Add materials for Python and REST APIs: Interacting With Web Services - #849

Open
realpython-bot wants to merge 1 commit into
masterfrom
materials/api-integration-in-python-20260916
Open

realpython-bot wants to merge 1 commit into
masterfrom
materials/api-integration-in-python-20260916

Conversation

@realpython-bot

Copy link
Copy Markdown
Collaborator

This is a brand-new materials folder for a tutorial that never had one, so it needs an editorial eye as well as a code review. Nothing here existed before: gh api repos/realpython/materials/contents/api-integration-in-python returned 404 prior to this branch.

Source of truth: the updated draft, not the live article — https://realpython.com/pbjt/blog/post/2308/change/
Maintenance card: https://trello.com/c/jdi8cLtG

What the folder contains

The examples are grouped into one subfolder per section, following the pattern used by siblings such as python-http-server/ and python-microservices-with-grpc/. Subfolders are necessary here because the Flask and FastAPI examples both use a file called app.py, and the tutorial itself advises readers to keep each example in its own folder with its own virtual environment.

Folder Article section
consuming-apis/ REST and Python: Consuming APIs (GET, POST, PUT, PATCH, DELETE)
flask-api/ Tools of the Trade → Flask
django-api/ Tools of the Trade → Django REST Framework
fastapi-api/ Tools of the Trade → FastAPI

REPL examples were turned into runnable scripts. The "Consuming APIs" section is written entirely as pycon blocks, so each HTTP-method subsection became one script that keeps the article's order and variable names (api_url, todo, headers, response) and calls print() where the REPL echo was the point:

  • consuming-apis/get_request.py
  • consuming-apis/post_request.py (both POST blocks — the json= version and the manual json.dumps() + Content-Type version)
  • consuming-apis/put_request.py
  • consuming-apis/patch_request.py
  • consuming-apis/delete_request.py

flask-api/app.py and fastapi-api/app.py are the article's # app.py blocks verbatim.

django-api/ is the countryapi project the article builds. The scaffolding was generated exactly as the article instructs (django-admin startproject countryapi, python manage.py startapp countries), then the article's own edits were applied: countryapi/settings.py (rest_framework and countries in INSTALLED_APPS), countries/models.py, countries/serializers.py, countries/views.py, countries/urls.py, countryapi/urls.py, and countries/fixtures/countries.json. The generated 0001_initial.py migration is committed, matching how other Django folders in this repo ship. db.sqlite3 is not committed. countries/admin.py and countries/tests.py carry # noqa: F401 on their untouched scaffold imports, the same as create-django-project/setup/example/.

No examples were invented and no code was "improved" — the folder mirrors the tutorial.

requirements.txt

Pins come straight from the draft's dependencies field (python==3.14 omitted, since that's the interpreter):

Django==6.1
Flask==3.1.3
djangorestframework==3.18.0
fastapi==0.141.1
requests==2.34.2
uvicorn==0.52.4

How this was verified

Everything below was actually run, on Python 3.14.6 in a fresh venv installed from the pins above. All pins resolved.

consuming-apis/ — all five scripts executed against the live JSONPlaceholder API. Output matches the article, e.g. get_request.py prints {'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}, then 200, then application/json; charset=utf-8. put_request.py prints the full title that the article abbreviates as 'illo est ... aut'.

flask-api/flask run started, then GET /countries returned the three seeded countries and POST /countries returned HTTP/1.1 201 CREATED with {"area":357022,"capital":"Berlin","id":4,"name":"Germany"}; the follow-up GET showed Germany appended. A non-JSON POST returned 415 UNSUPPORTED MEDIA TYPE with {"error":"Request must be JSON"}. All match the article.

django-api/python manage.py makemigrations printed + Create model Country, migrate applied cleanly, loaddata countries.json printed Installed 3 object(s) from 1 fixture(s), and manage.py check reported no issues. With runserver up: GET /countries/, POST /countries/ (201 Created, {"id":4,...}), and GET /countries/1/ all matched the article.

fastapi-api/uvicorn app:app started, GET /countries and POST /countries (201 Created) matched, and the deliberately invalid POST without area returned the article's validation payload: {"detail":[{"type":"missing","loc":["body","area"],"msg":"Field required","input":{"name":"Germany","capital":"Berlin"}}]}.

Repo gates at the version the root requirements.txt pins:

$ uvx ruff@0.14.1 format --check api-integration-in-python
21 files already formatted
$ uvx ruff@0.14.1 check api-integration-in-python
All checks passed!

Nothing was skipped: there is no GUI, plotting, paid API key, or unavailable service anywhere in this tutorial's code.

One drift worth an editor's attention (article-side, not fixed here)

On the pinned stack (fastapi==0.141.1 / starlette==1.6.0), the invalid-POST example returns the reason phrase 422 Unprocessable Content, while the draft still shows 422 Unprocessable Entity. The status code and JSON body are unchanged. That's an article edit, and the CMS was read-only for this task, so it's flagged here rather than changed.

🤖 Generated with Claude Code

Create the api-integration-in-python folder, which the tutorial never had.
It holds the requests examples from the "Consuming APIs" section as
runnable scripts and the Flask, Django REST framework, and FastAPI
countries APIs from the "Tools of the Trade" section.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant