{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "67a52fd4-ddb8-4bc4-95e1-d274446ce83f",
   "metadata": {},
   "source": [
    "## FastAPI"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1b74a16-d8c4-4b17-9c57-9c7af0c86293",
   "metadata": {},
   "source": [
    "## Installation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "377f76a1-856b-4c99-9963-d117b325ea0e",
   "metadata": {},
   "outputs": [],
   "source": [
    "#pip install fastapi\n",
    "#pip install \"uvicorn[standard]\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f23aa689-4de2-4548-9acb-62f96bb975e1",
   "metadata": {},
   "source": [
    "## Quick example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faffc64a-dd2a-4a22-8850-ee65a2e33edf",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Union\n",
    "\n",
    "from fastapi import FastAPI\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "\n",
    "@app.get(\"/\")\n",
    "def read_root():\n",
    "    return {\"Hello\": \"World\"}\n",
    "\n",
    "\n",
    "@app.get(\"/items/{item_id}\")\n",
    "def read_item(item_id: int, q: Union[str, None] = None):\n",
    "    return {\"item_id\": item_id, \"q\": q}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d325f79-0753-4cde-888e-7c6188b14d46",
   "metadata": {},
   "source": [
    "## Asynchronous version\n",
    "* In you are using third party libraries that tell you to call them with `await`, use `async def` instead of `def`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "306476d4-caab-4fa3-a912-0c6af2508a06",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Union\n",
    "\n",
    "from fastapi import FastAPI\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "\n",
    "@app.get(\"/\")\n",
    "async def read_root():\n",
    "    results = await some_library()\n",
    "    return results"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd8fbe49-06f4-40ed-8dc3-6bad412707cd",
   "metadata": {},
   "source": [
    "#### When to use asyncronous code?\n",
    "* \"Synchronous\" or \"sequential\" code means that the computer / program follows all the steps in sequence before switching to a different task, even if those steps involve waiting.\n",
    "* Asynchronous code just means that the language has a way to tell the computer / program that at some point in the code, it will have to wait for something else to finish somewhere else. Let's say that something else is called \"slow-file\". So, during that time, the computer can go and do some other work, while \"slow-file\" finishes.\n",
    "* That \"wait for something else\" normally refers to I/O operations that are relatively \"slow\" (compared to the speed of the processor and the RAM memory), like waiting for:\n",
    "    * the data from the client to be sent through the network\n",
    "    * the data sent by your program to be received by the client through the network\n",
    "    * the contents of a file in the disk to be read by the system and given to your program\n",
    "    * the contents your program gave to the system to be written to disk\n",
    "    * a remote API operation\n",
    "    * a database operation to finish\n",
    "    * a database query to return the results\n",
    "    * etc."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50513f97-493e-434e-b00e-e68b6e31d571",
   "metadata": {},
   "source": [
    "## Run the code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "28a8d7c5-382f-4eb4-b1b1-c4e4096c1b8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "##uvicorn main:app --reload"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "716956a7-d92e-4417-bf26-ca4c053cd636",
   "metadata": {},
   "source": [
    "## Check it in your browser\n",
    "* http://127.0.0.1:8000\n",
    "* http://127.0.0.1:8000/items/5?q=somequery"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9c3ccfea-c267-482a-b607-f660f0f071f2",
   "metadata": {},
   "source": [
    "## API docs\n",
    "* http://127.0.0.1:8000/docs\n",
    "* http://127.0.0.1:8000/redoc"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9da6623c-e623-4423-b0a8-a1921cdd61b5",
   "metadata": {},
   "source": [
    "## BaseModel, Filter, Parameters and Queries"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "311972d8-72b1-4ee1-b637-c9123ec6e957",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Union\n",
    "from fastapi import FastAPI\n",
    "from models.item import Item\n",
    "\n",
    "app = FastAPI()\n",
    "\n",
    "items = [\n",
    "    {\n",
    "        \"id\": 1,\n",
    "        \"name\": \"iphone\",\n",
    "        \"price\": 950,\n",
    "        \"is_offer\": False\n",
    "    },\n",
    "    {\n",
    "        \"id\": 2,\n",
    "        \"name\": \"samsung\",\n",
    "        \"price\": 750,\n",
    "        \"is_offer\": True\n",
    "    }\n",
    "]\n",
    "\n",
    "@app.get(\"/\")\n",
    "def read_root():\n",
    "    return {\"Hello\": \"World\"}\n",
    "\n",
    "\n",
    "@app.post(\"/items\")\n",
    "def create_item(item: Item):\n",
    "    items.append(item)\n",
    "    return items\n",
    "\n",
    "@app.get(\"/items\")\n",
    "def get_items():\n",
    "    return items\n",
    "\n",
    "@app.get(\"/items/\")\n",
    "def get_items_by_name(name: str):\n",
    "    return list(filter(lambda item: item['name'] == name, items))\n",
    "\n",
    "@app.get(\"/items/{item_id}\")\n",
    "def read_item(item_id: int, q: Union[str, None] = None):\n",
    "    return {\"item_id\": item_id, \"q\": q}\n",
    "\n",
    "@app.put(\"/items/{item_id}\")\n",
    "def update_item(item_id: int, item: Item):\n",
    "    return {\n",
    "        \"id\": item_id,\n",
    "        \"name\": item.name,\n",
    "        \"price\": item.price,\n",
    "        \"is_offer\": item.is_offer\n",
    "        }\n",
    "\n",
    "@app.put(\"/items/update/{item_id}\")\n",
    "def second_update_item(item_id: int, item: Item):\n",
    "    for index, item in enumerate(items):\n",
    "        if item[\"id\"] == item_id:\n",
    "            item[index][\"name\"] = item.name\n",
    "            item[index][\"price\"] = item.price\n",
    "            item[index][\"is_offer\"] = item.is_offer\n",
    "    return items\n",
    "\n",
    "@app.delete(\"/items/{item_id}\")\n",
    "def eliminate_item(item_id: int):\n",
    "    for item in items:\n",
    "        if item[\"id\"] ==item_id:\n",
    "            items.remove(item)\n",
    "    return items"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cabdebe2-7503-43cf-ba8f-7641e09fbe9c",
   "metadata": {},
   "source": [
    "## Schemas\n",
    "* Model definition with BaseModel from Pydantic.\n",
    "* Type validation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "c719295c-407c-4928-93d6-d5991f1cdfc9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from typing import Union, Optional\n",
    "from pydantic import BaseModel\n",
    "\n",
    "class Item(BaseModel):\n",
    "    id: int\n",
    "    name: str\n",
    "    price: Optional[float] = None\n",
    "    is_offer: Union[bool, None] = None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a622c505-7b13-4993-b798-dd3ec9a18e23",
   "metadata": {},
   "source": [
    "## Routers: when we have more than one backend API\n",
    "* For example, item API and user API\n",
    "* We will create routers/item.py and routers/user.py\n",
    "* Below is item.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "17027348-420c-4e6e-93ca-b6f93a70212b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from fastapi import APIRouter, Path\n",
    "from models.item import Item\n",
    "from typing import Union\n",
    "\n",
    "router = APIRouter()\n",
    "\n",
    "items = [\n",
    "    {\n",
    "        \"id\": 1,\n",
    "        \"name\": \"iphone\",\n",
    "        \"price\": 950,\n",
    "        \"is_offer\": False\n",
    "    },\n",
    "    {\n",
    "        \"id\": 2,\n",
    "        \"name\": \"samsung\",\n",
    "        \"price\": 750,\n",
    "        \"is_offer\": True\n",
    "    }\n",
    "]\n",
    "\n",
    "@router.get(\"/items/{item_id}\")\n",
    "def read_item(item_id: int = Path(ge=0), q: Union[str, None] = None):\n",
    "    return {\"item_id\": item_id, \"q\": q}\n",
    "\n",
    "@router.put(\"/items/{item_id}\")\n",
    "def update_item(item_id: int, item: Item):\n",
    "    return {\n",
    "        \"id\": item_id,\n",
    "        \"name\": item.name,\n",
    "        \"price\": item.price,\n",
    "        \"is_offer\": item.is_offer\n",
    "        }\n",
    "\n",
    "@router.put(\"/items/update/{item_id}\")\n",
    "def second_update_item(item_id: int, item: Item):\n",
    "    for index, item in enumerate(items):\n",
    "        if item[\"id\"] == item_id:\n",
    "            item[index][\"name\"] = item.name\n",
    "            item[index][\"price\"] = item.price\n",
    "            item[index][\"is_offer\"] = item.is_offer\n",
    "    return items\n",
    "\n",
    "@router.get(\"/items/\")\n",
    "def get_items_by_name(name: str):\n",
    "    return list(filter(lambda item: item['name'] == name, items))\n",
    "\n",
    "@router.get(\"/items\")\n",
    "def get_items():\n",
    "    return items\n",
    "\n",
    "@router.post(\"/items\")\n",
    "def create_item(item: Item):\n",
    "    items.append(item)\n",
    "    return items\n",
    "\n",
    "@router.delete(\"/items/{item_id}\")\n",
    "def eliminate_item(item_id: int):\n",
    "    for item in items:\n",
    "        if item[\"id\"] ==item_id:\n",
    "            items.remove(item)\n",
    "    return items"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "72dd92d1-2102-4c87-87d4-ecec8a3a7c0b",
   "metadata": {},
   "source": [
    "After this, we can remove the endpoints from main.py and include there:\n",
    "* app.include_router(item_router)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "aecd2d6b-9cce-4a55-9230-9d91e18e8a2d",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
