Basic principles and practical techniques to use REST APIs in Python

REST in Python course

A hands-on, interactive training program designed to equip developers with practical expertise in building and consuming RESTful APIs using Python. Delivered through live Jupyter Notebooks, the course covers foundational concepts, like REST constraints, HTTP methods, headers, payloads and status codes, but is delivered as a real-world workshop. Tad Lispy guides learners through authentic scenarios, like user authentication, CRUD operations, optimistic locking with ETags, and secure resource management. Using the requests library and a custom HTTP inspection tool, participants gain immediate, tangible experience with HTTP APIs. We interact with services like JaaS (Jokes as a Service) and JSONPlaceholder to give participants practical, working skills. With a modern development environment powered by Nix-based devenv and Python 3, this course is ideal for teams seeking to standardize their API practices while fostering a collaborative, engaging learning atmosphere.

Latest developments

from see older.

Update URLs to Joke as a Service

On by Tad Lispy

index 2be67b6..f49b0fd 100644
--- a/index.ipynb
+++ b/index.ipynb
@@ -100,7 +100,7 @@
=   "source": [
=    "## Exercise: Let's see it in action\n",
=    "\n",
-    "Navigate your browser to http://tedros-hagos.com:8000/docs/ and try to use the provided UI to:\n",
+    "Navigate your browser to http://jaas.tedros-hagos.com/docs/ and try to use the provided UI to:\n",
=    "\n",
=    "  1. List all jokes\n",
=    "  2. Register a new user (joker) and share a few funny jokes.\n",
@@ -136,9 +136,9 @@
=    "import requests\n",
=    "from IPython.display import JSON\n",
=    "\n",
-    "jokes_url = \"http://tedros-hagos.com:8000/\"\n",
+    "jokes_url = \"https://jaas.tedros-hagos.com\"\n",
=    "\n",
-    "response = requests.get (jokes_url + \"jokes/?sort=author_name\")\n",
+    "response = requests.get (f\"{jokes_url}/jokes/?sort=author_name\")\n",
=    "\n",
=    "JSON (response.json())"
=   ]

Split the long list of methods into cells

On by Tad Lispy

It was causing rendering issues in some browsers.

Also apply small edits.

index f49b0fd..8bc191e 100644
--- a/index.ipynb
+++ b/index.ipynb
@@ -117,7 +117,7 @@
=   "source": [
=    "## Workshop: Let's write some code\n",
=    "\n",
-    "Here is a starter code to get jokes sorted by author name. How would you change it to get the least funny jokes first?"
+    "Here is a starter code to get jokes sorted by author name. Click on the code block and press <kbd>shift</kbd>+<kbd>enter</kbd> to run it. How would you change it to get the least funny jokes first? Double click to edit."
=   ]
=  },
=  {
@@ -418,72 +418,132 @@
=  },
=  {
=   "cell_type": "markdown",
-   "id": "fea55693-70bf-4619-be96-865e101309af",
+   "id": "3fa63afa-d128-4560-90f2-e8651cf58f7c",
=   "metadata": {},
=   "source": [
=    "## Methods (Verbs)\n",
=    "\n",
-    "These are commonly used methods. New methods can (and historically were) introduced.  Server are only required to implement the GET and HEAD methods.\n",
+    "These are commonly used methods. New methods can (and historically were) introduced.  Server are only required to implement the GET and HEAD methods."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e956526f-e045-4fd9-9fc4-c5caa1f42ce5",
+   "metadata": {},
+   "source": [
+    "### GET\n",
+    "  \n",
+    "Retrieve a resource"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5eba70dc-e532-4d04-b934-19ce4e7f79fc",
+   "metadata": {},
+   "source": [
+    "### HEAD\n",
=    "\n",
+    "Like get, but the response should come without a body. Useful for checking if a resource exists or getting it's metadata (like size or modification time)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e55da8b0-1a6a-48b6-963e-5ed85c2200d6",
+   "metadata": {},
+   "source": [
+    "### POST\n",
=    "\n",
-    "  * GET\n",
=    "  \n",
-    "    Retrieve a resource\n",
+    "Store a new resource or start a non-idempotent process. The URL typically points to an existing resource, for example a collection. The target resource is \"responsible\" for creating the new one. For example:\n",
=    "  \n",
-    "  * HEAD\n",
-    "  \n",
-    "    Like get, but the response should come without a body. Useful for checking if a resource exists or getting it's metadata (like size or modification time).\n",
-    "  \n",
-    "  * POST\n",
-    "  \n",
-    "    Store a new resource or start a non-idempotent process. The URL typically points to an existing resource, for example a collection. The target resource is \"responsible\" for creating the new one. For example:\n",
-    "  \n",
-    "    ``` http\n",
-    "    POST https://example.com/products HTTP/1.1\n",
-    "    \n",
-    "    REST-o-Matic 2000\n",
-    "    ```\n",
+    "``` http\n",
+    "POST https://example.com/products HTTP/1.1\n",
+    "\n",
+    "REST-o-Matic 2000\n",
+    "```\n",
=    "  \n",
-    "    Response:\n",
+    "Response:\n",
=    "    \n",
-    "    ``` http\n",
-    "    200 OK HTTP/1.1\n",
-    "    Location:  /products/1337-rest-o-matic-2000.html\n",
-    "    ```\n",
+    "``` http\n",
+    "200 OK HTTP/1.1\n",
+    "Location:  /products/1337-rest-o-matic-2000.html\n",
+    "```\n",
=    "    \n",
-    "    Here target resource is `/products` (a collection) and the new resource is `/products/1337-rest-o-matic-2000.html`, as indicated by the server in the `Location` response header.\n",
-    "  \n",
-    "  \n",
-    "  * PUT\n",
-    "  \n",
-    "    Replace or update an existing resource, or create a new one at the given location.  Therefore the URL must point to the target resource (unlike POST).\n",
-    "  \n",
-    "  \n",
-    "  * PATCH\n",
-    "  \n",
-    "    Update an existing resource with a partial representation in the request body.\n",
-    "  \n",
-    "  \n",
-    "  * DELETE\n",
-    "  \n",
-    "    Remove an existing resource\n",
-    "  \n",
-    "  \n",
-    "  * OPTIONS\n",
-    "  \n",
-    "    Inquire about a resource.\n",
-    "  \n",
-    "  \n",
-    "  * CONNECT\n",
-    "  \n",
-    "    Request estblishing a TCP/IP tunel. See <https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_method>.\n",
+    "Here target resource is `/products` (a collection) and the new resource is `/products/1337-rest-o-matic-2000.html`, as indicated by the server in the `Location` response header."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "9719f3b7-b3b1-4f0f-b060-f5f6ddf7a104",
+   "metadata": {},
+   "source": [
+    "### PUT\n",
=    "  \n",
+    "Replace or update an existing resource, or create a new one at the given location.  Therefore the URL must point to the target resource (unlike POST).\n",
+    "  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c2d02be5-ff19-49ff-bfd0-e30dfd94fce7",
+   "metadata": {},
+   "source": [
+    "### PATCH\n",
=    "  \n",
-    "  * TRACE\n",
+    "Update an existing resource with a partial representation in the request body."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fae79372-7bcd-4f1f-bc7d-65a839646f7c",
+   "metadata": {},
+   "source": [
+    "### DELETE\n",
=    "  \n",
-    "    Like an echo. The TRACE method requests that the target resource transfer the received request in the response body. That way a client can see what (if any) changes or additions have been made by intermediaries.\n",
+    "Remove an existing resource\n",
+    "  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a2390699-78d4-419e-a5c3-850ec1c826a1",
+   "metadata": {},
+   "source": [
+    "### OPTIONS\n",
+    "\n",
+    "Inquire about a resource.\n",
+    "  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "13c88f77-c1ec-45be-aec7-e9005d838ad0",
+   "metadata": {},
+   "source": [
+    "### CONNECT\n",
=    "\n",
-    "> Exercise: Use different methods with the example API. Discuss."
+    "Request estblishing a TCP/IP tunel. See <https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_method>.\n",
+    "  "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "8a6e32d7-2053-46ab-b694-08b3deacb891",
+   "metadata": {},
+   "source": [
+    "### TRACE\n",
+    "\n",
+    "Like an echo. The TRACE method requests that the target resource transfer the received request in the response body. That way a client can see what (if any) changes or additions have been made by intermediaries."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c4b69e14-39d6-43c6-9e55-59bcff160604",
+   "metadata": {},
+   "source": [
+    "### Exercise\n",
+    "\n",
+    "Use different methods with the example API. Discuss."
=   ]
=  },
=  {
@@ -561,7 +621,7 @@
=   "metadata": {},
=   "outputs": [],
=   "source": [
-    "HTTP (requests.get(\"https://tad-lispy.com/\", params={ \"hello\": True, \"name\": \"Tad 'Lispy' Łazurski\" }))"
+    "HTTP (requests.head(\"https://tad-lispy.com/\", params={ \"hello\": True, \"name\": \"Tad 'Lispy' Łazurski\" }))"
=   ]
=  },
=  {
@@ -661,17 +721,6 @@
=    "> Exercise: Try to get different status codes from the example API.\n"
=   ]
=  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "id": "0f018758-9591-4094-a0d2-fe41a2716290",
-   "metadata": {},
-   "outputs": [],
-   "source": [
-    "response = requests.get (jokes_url + \"/random_joke/\")\n",
-    "HTTP (response)"
-   ]
-  },
=  {
=   "cell_type": "markdown",
=   "id": "163f31ea-9563-4b51-a915-ffaae71ae108",

Older Devlog Entries