Add materials for Python and REST APIs: Interacting With Web Services - #849
Open
realpython-bot wants to merge 1 commit into
Open
realpython-bot wants to merge 1 commit into
realpython-bot wants to merge 1 commit into
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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-pythonreturned 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/andpython-microservices-with-grpc/. Subfolders are necessary here because the Flask and FastAPI examples both use a file calledapp.py, and the tutorial itself advises readers to keep each example in its own folder with its own virtual environment.consuming-apis/flask-api/django-api/fastapi-api/REPL examples were turned into runnable scripts. The "Consuming APIs" section is written entirely as
pyconblocks, so each HTTP-method subsection became one script that keeps the article's order and variable names (api_url,todo,headers,response) and callsprint()where the REPL echo was the point:consuming-apis/get_request.pyconsuming-apis/post_request.py(both POST blocks — thejson=version and the manualjson.dumps()+Content-Typeversion)consuming-apis/put_request.pyconsuming-apis/patch_request.pyconsuming-apis/delete_request.pyflask-api/app.pyandfastapi-api/app.pyare the article's# app.pyblocks verbatim.django-api/is thecountryapiproject 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_frameworkandcountriesinINSTALLED_APPS),countries/models.py,countries/serializers.py,countries/views.py,countries/urls.py,countryapi/urls.py, andcountries/fixtures/countries.json. The generated0001_initial.pymigration is committed, matching how other Django folders in this repo ship.db.sqlite3is not committed.countries/admin.pyandcountries/tests.pycarry# noqa: F401on their untouched scaffold imports, the same ascreate-django-project/setup/example/.No examples were invented and no code was "improved" — the folder mirrors the tutorial.
requirements.txtPins come straight from the draft's
dependenciesfield (python==3.14omitted, since that's the interpreter):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.pyprints{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}, then200, thenapplication/json; charset=utf-8.put_request.pyprints the full title that the article abbreviates as'illo est ... aut'.flask-api/—flask runstarted, thenGET /countriesreturned the three seeded countries andPOST /countriesreturnedHTTP/1.1 201 CREATEDwith{"area":357022,"capital":"Berlin","id":4,"name":"Germany"}; the follow-upGETshowed Germany appended. A non-JSONPOSTreturned415 UNSUPPORTED MEDIA TYPEwith{"error":"Request must be JSON"}. All match the article.django-api/—python manage.py makemigrationsprinted+ Create model Country,migrateapplied cleanly,loaddata countries.jsonprintedInstalled 3 object(s) from 1 fixture(s), andmanage.py checkreported no issues. Withrunserverup:GET /countries/,POST /countries/(201 Created,{"id":4,...}), andGET /countries/1/all matched the article.fastapi-api/—uvicorn app:appstarted,GET /countriesandPOST /countries(201 Created) matched, and the deliberately invalidPOSTwithoutareareturned 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.txtpins: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-POSTexample returns the reason phrase422 Unprocessable Content, while the draft still shows422 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