{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "8a265247-42b1-4d16-867a-c1c7fca8f013",
   "metadata": {},
   "source": [
    "## Full Stack App with FastAPI and Vercel\n",
    "* We will create an app that manages a to-do list, a list of tasks that you want to complete."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4c322d30-f1c2-4afb-8d93-afa5a1f6deba",
   "metadata": {},
   "source": [
    "#### As always, it is recommended to create a virtual environment"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b3585640-e78c-46ee-9500-97d5843de364",
   "metadata": {},
   "source": [
    "## Part 1: Backend with FastAPI and Postgres"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ef4b2df-c9ab-406d-8f11-91d1e65c8526",
   "metadata": {},
   "source": [
    "#### Create the app folder"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4401ca05-cecf-4c15-a556-0398ab4a1a21",
   "metadata": {},
   "source": [
    "#### Inside the app folder, create the backend folder"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "87298130-ca35-436c-940e-9be44a004735",
   "metadata": {},
   "source": [
    "#### You will need to have Postgres installed"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "d6286c92-d964-46d9-9f48-b50fb04c6333",
   "metadata": {},
   "outputs": [],
   "source": [
    "#brew install postgresql\n",
    "#brew services start postgresql"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "de7eb839-df2f-4dbb-a9d3-d3c643773b6c",
   "metadata": {},
   "source": [
    "#### Install the necessary packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "b5205418-5831-4ee5-bd1b-96866e5b691d",
   "metadata": {},
   "outputs": [],
   "source": [
    "#pip install fastapi \"uvicorn[standard]\" alembic psycopg2 pytest requests pydantic_settings"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4b94aeb-991e-495b-bf20-d95a06cc3cb1",
   "metadata": {},
   "source": [
    "#### Save them in requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "f516344f-bc29-4779-a5bb-a5afea2fb949",
   "metadata": {},
   "outputs": [],
   "source": [
    "#pip freeze > requirements.txt"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "385ffd71-e23a-4d2b-aa21-d16b6fe45e88",
   "metadata": {},
   "source": [
    "#### Create the Postgress database"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "5ab3aa9a-5d98-4c17-ab70-ad3c9fbb0859",
   "metadata": {},
   "outputs": [],
   "source": [
    "#createdb mydatabase"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30c4b925-d5c1-402d-94c6-9e102e221459",
   "metadata": {},
   "source": [
    "#### Inside the backend folder, create the .env file and enter DB credentials like this"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0ed256b9-ca63-4dc1-a68a-3f709f56b683",
   "metadata": {},
   "outputs": [],
   "source": [
    "# DATABASE_HOST=localhost\n",
    "# DATABASE_NAME=mydatabase\n",
    "# DATABASE_USER=postgres\n",
    "# DATABASE_PASSWORD=\n",
    "# DATABASE_PORT=5432\n",
    "# APP_NAME=\"Full Stack To Do App\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83145dca-9332-41e6-8316-eaab7807926e",
   "metadata": {},
   "source": [
    "#### Create config.py file\n",
    "* Pydantic settings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "109fda92-e7d2-4b30-897a-cf666beaf02c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from pydantic_settings import BaseSettings\n",
    "\n",
    "# class Settings(BaseSettings):\n",
    "#     DATABASE_HOST: str\n",
    "#     DATABASE_NAME: str\n",
    "#     DATABASE_USER: str\n",
    "#     DATABASE_PASSWORD: str\n",
    "#     DATABASE_PORT: int\n",
    "#     app_name: str = \"Full Stack To Do App\"\n",
    "\n",
    "#     class Config:\n",
    "#         env_file = \".env\"\n",
    "#         extra = \"ignore\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6a7ae7c1-b0aa-418f-96a1-048153b462a0",
   "metadata": {},
   "source": [
    "#### Create main.py file\n",
    "* Create app\n",
    "* CORS configuration for next frontend development\n",
    "* Global HTTP exception handler\n",
    "* Two endpoints:\n",
    "    * Root (home page)\n",
    "    * items/{item_id}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "831fa83d-efc0-4dc4-bf0a-d6398e9d38a6",
   "metadata": {},
   "source": [
    "#### Start server"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c3cf7b8d-2ef2-440f-adcf-d625afffd768",
   "metadata": {},
   "outputs": [],
   "source": [
    "#uvicorn main:app --reload"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2062bb63-8677-4e01-aa88-1637e2b117bd",
   "metadata": {},
   "source": [
    "#### Check the app in http://127.0.0.1:8000/\n",
    "* In the terminal, check if the app name is printed"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4edb229d-41bc-4538-8f1d-ebb826689932",
   "metadata": {},
   "source": [
    "#### Alternative way to check the app from a second window of your terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "ae5766a3-83ab-4d38-a938-b59ab9a3246a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#curl http://localhost:8000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57af7105-9098-4761-8aa0-96d3e2b7917f",
   "metadata": {},
   "source": [
    "#### Another alternative to check the app from a second window of your terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "19cef3a4-ded6-4e86-86e6-d4841ca30778",
   "metadata": {},
   "outputs": [],
   "source": [
    "#pip install httpie\n",
    "#http http://localhost:8000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "27753359-5d99-4cc2-b3f6-2a9531e7382e",
   "metadata": {},
   "source": [
    "#### After the initial check, now close the app and set the database migrations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "72a8e4a3-29c9-4d5b-a544-13925017a622",
   "metadata": {},
   "outputs": [],
   "source": [
    "#ctrl-c to stop the server in the terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "0240cdd0-da5f-4ed3-ab19-8bed11d0f82e",
   "metadata": {},
   "outputs": [],
   "source": [
    "#alembic init alembic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "823eea02-e869-4920-9e79-05434f12c294",
   "metadata": {},
   "source": [
    "The `alembic init alembic` command is used to initialize Alembic in a project. Alembic is a database migration library for Python, commonly used with SQLAlchemy (an object-relational mapping tool for Python). This command sets up Alembic so it can be used to handle database versioning in your project.\n",
    "\n",
    "When you run `alembic init alembic`, the following happens:\n",
    "\n",
    "1. **Creation of Directory Structure**: The command creates a new directory named `alembic` in your project. Within this directory, Alembic stores migration scripts and some configuration files.\n",
    "\n",
    "2. **Configuration File**: It generates an `alembic.ini` file in the root directory of your project. This file contains the necessary configuration for Alembic to connect to your database and other relevant settings.\n",
    "\n",
    "3. **Versions Directory**: Inside the `alembic` directory, a subdirectory named `versions` is created. This directory will house the individual migration scripts you create to modify your database (for example, to add tables, change schemas, etc.).\n",
    "\n",
    "4. **`env.py` File**: An `env.py` file is also created in the `alembic` directory. This file is the entry point for Alembic and is used to configure the migration context, database connection, and other aspects of the migration environment.\n",
    "\n",
    "The purpose of using Alembic is to facilitate tracking and applying changes to the database schema in a controlled and consistent manner. It allows for incremental versions to be applied to the database, which is crucial in development, testing, and production environments, especially in large teams where multiple developers may be making changes to the database."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "875e6b66-04ce-4eed-8e90-fc46c7adfe76",
   "metadata": {},
   "source": [
    "#### Edit alembic/env.py\n",
    "* To have access to the .env file, add the following lines:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c253f7af-e297-423b-b816-7ffe727f7b1e",
   "metadata": {},
   "outputs": [],
   "source": [
    "#from dotenv import load_dotenv\n",
    "#load_dotenv()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bbe6b049-019d-4b73-af59-8ef846b0c3c5",
   "metadata": {},
   "source": [
    "Insert next line after line 13:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "edc93a6c-cbb8-46c3-8544-3f2a505e5cb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "#import os\n",
    "#config.set_main_option(\"sqlalchemy.url\", f\"postgresql://{os.environ['DATABASE_USER']}:@{os.environ['DATABASE_HOST']}:{os.environ['DATABASE_PORT']}/{os.environ['DATABASE_NAME']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0a7b1f1-090e-4ccc-85a2-dc296180a0cd",
   "metadata": {},
   "source": [
    "#### Create todos table from terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "871b2512-e54d-4d5d-9284-bc4c5e5641af",
   "metadata": {},
   "outputs": [],
   "source": [
    "#alembic revision -m \"create todos table\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b089570-2ce2-4aad-8f05-1810f7275a9d",
   "metadata": {},
   "source": [
    "#### Check the updates in alambic/versions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e64b1350-797e-492d-bd9d-c9f529ec50e4",
   "metadata": {},
   "source": [
    "#### Edit xxx_create_todos_table.py to define the schema of the new table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f130691e-86c2-4de5-a416-0255134f0d08",
   "metadata": {},
   "outputs": [],
   "source": [
    "# def upgrade():\n",
    "#     op.execute(\"\"\"\n",
    "#     create table todos(\n",
    "#         id bigserial primary key,\n",
    "#         name text,\n",
    "#         completed boolean not null default false\n",
    "#     )\n",
    "#     \"\"\")\n",
    "\n",
    "# def downgrade():\n",
    "#     op.execute(\"drop table todos;\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ed83c9af-c5a2-486d-b215-721aa4ed5571",
   "metadata": {},
   "source": [
    "#### Run the migration from terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e47ccdf2-94d3-4158-a503-c795201bb17e",
   "metadata": {},
   "outputs": [],
   "source": [
    "#psql -d mydatabase"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "d5bf3c9c-dff7-4b41-8263-b21e520b7827",
   "metadata": {},
   "outputs": [],
   "source": [
    "#CREATE USER user12 WITH PASSWORD 'pass12';"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "da96adba-4a0b-4901-9c26-562953c5fc8f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#GRANT ALL PRIVILEGES ON DATABASE mydatabase TO user12;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3e9695c0-4c5e-4916-b773-fd673f4bab8d",
   "metadata": {},
   "source": [
    "NOTE: Our Honor Student **Robert Merchant** discovered that in the last PostgreSQL version (v. 15) is necessary to add also the following line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "f5af387f-aba1-4c78-9026-0ed36d4eb4cb",
   "metadata": {},
   "outputs": [],
   "source": [
    "#GRANT ALL PRIVILEGES ON SCHEMA public TO user12;"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3933b124-f53e-49ff-8f48-c355ad7aeb89",
   "metadata": {},
   "source": [
    "You can find more info about this PostgreSQL update [here](https://stackoverflow.com/questions/67276391/why-am-i-getting-a-permission-denied-error-for-schema-public-on-pgadmin-4).\n",
    "\n",
    "Thanks Robert!!! You make us all better :)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f517a363-f51a-42ce-8e60-827db166211e",
   "metadata": {},
   "outputs": [],
   "source": [
    "#\\q"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13250742-3029-4a98-a72a-65ec5e4a985f",
   "metadata": {},
   "source": [
    "Alternativa a lo anterior:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c8c23549-8999-4ae7-a081-e6ce36250251",
   "metadata": {},
   "outputs": [],
   "source": [
    "#psql -U your_superadmin_username -h localhost -d mydatabase"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8705bac0-13d4-4b0b-9d72-86a92e978713",
   "metadata": {},
   "source": [
    "#### Edit this line in alembic.ini"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec9b4f57-9deb-452f-9128-7d00c8e1d97a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#sqlalchemy.url = postgresql://user12:pass12@localhost/mydatabase"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9f4f3c26-a70c-438e-be6c-b13d14f6c342",
   "metadata": {},
   "source": [
    "#### Edit .env"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8079d8cf-ca50-4370-b043-42bac20869aa",
   "metadata": {},
   "outputs": [],
   "source": [
    "# DATABASE_USER=user12\n",
    "# DATABASE_PASSWORD=pass12"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b25c3def-73c6-41af-9983-78fb216edd9a",
   "metadata": {},
   "source": [
    "#### Run the database migration from terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "12241ec1-4dd4-442b-a6c2-6d8ccb0452e9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#alembic upgrade head"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8a36a7d-3531-43b0-8666-0e5aa8685135",
   "metadata": {},
   "source": [
    "#### Check the database in terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "400f4882-e1f1-4617-847d-7c03118a4a3a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#psql mydatabase\n",
    "#\\dt\n",
    "#select * from todos\n",
    "#\\q"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9d844f73-c7be-4a18-819e-4c93bab3c0a9",
   "metadata": {},
   "source": [
    "#### Set up the schemas\n",
    "* Create the file schemas.py in the root directory of the app\n",
    "* It defines the data model: data structure, type, validation and extra configuration\n",
    "* ToDoRequest is a data model with 2 data types\n",
    "* ToDoResponse is a data model with 3 data types\n",
    "* Config defines an extra configuration in ToDoResponse\n",
    "    * orm_mode = True allows the model to work with ORMs like SQLAlchemy. Useful when we retrieve data from a database using a ORM (Object-Relational Mapping) and deliver those data in a structured format through an API.\n",
    "    * because we want to serialize our database entities (convert Python objects into JSON format)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "2bb9a8b7-ea8a-44be-a44d-2b70398cc25b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from pydantic import BaseModel\n",
    "\n",
    "# class ToDoRequest(BaseModel):\n",
    "#     name: str\n",
    "#     completed: bool\n",
    "\n",
    "# class ToDoResponse(BaseModel):\n",
    "#     name: str\n",
    "#     completed: bool\n",
    "#     id: int\n",
    "\n",
    "#     class Config:\n",
    "#         orm_mode = True"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "935df77f-20d9-481f-9ff9-dbcfcb65feaf",
   "metadata": {},
   "source": [
    "#### Create the ORM (Object-Relational Mapping)\n",
    "* Create the file database.py in the root directory of the app\n",
    "* create_engine connects with the database\n",
    "* sessionmaker allows to interact with the database\n",
    "* declarative_base creates a base class for the models of the database, that will be all the tables"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "a447da7d-620e-45ba-bc00-23bf3337e8e8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import os\n",
    "# from sqlalchemy import create_engine\n",
    "# from sqlalchemy.ext.declarative import declarative_base\n",
    "# from sqlalchemy.orm import sessionmaker\n",
    "# from dotenv import load_dotenv\n",
    "\n",
    "# load_dotenv()\n",
    "\n",
    "# SQLALCHEMY_DATABASE_URL = f\"postgresql://{os.environ['DATABASE_USER']}:@{os.environ['DATABASE_HOST']}/{os.environ['DATABASE_NAME']}\"\n",
    "\n",
    "# engine = create_engine(\n",
    "#     SQLALCHEMY_DATABASE_URL\n",
    "# )\n",
    "# SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)\n",
    "\n",
    "# Base = declarative_base()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1afdc012-70b8-49c8-ba86-f86005b018b0",
   "metadata": {},
   "source": [
    "#### Create models.py\n",
    "* This is where we will define the database model using SQLAlquemy, a ORM library very popular in Python.\n",
    "* Using SQLAlchemy we can interact with the table usign ToDo objects instead of writing SQL commands manually.\n",
    "* The model represents the table ToDo in a database.\n",
    "* Column, Integer, String and Boolean are column types.\n",
    "* `__tablename__` assigns a name to the table"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "99f7de74-552a-44f9-8f0a-c6b9c7a4574b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from sqlalchemy import Boolean, Column, ForeignKey, Integer, String\n",
    "# from sqlalchemy.orm import relationship\n",
    "\n",
    "# from database import Base\n",
    "\n",
    "\n",
    "# class ToDo(Base):\n",
    "#     __tablename__ = \"todos\"\n",
    "\n",
    "#     id = Column(Integer, primary_key=True, index=True)\n",
    "#     name = Column(String)\n",
    "#     completed = Column(Boolean, default=False)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "73c466a2-102a-40af-bb98-27b330b9a951",
   "metadata": {},
   "source": [
    "#### Create crud.py\n",
    "* With our CRUD (Create, Read, Update, Delete) helpers\n",
    "* We import the `model` and `schema` we have defined earlier\n",
    "* `Session` is used to manage the database operations\n",
    "* `create_todo` creates a new todo task in the dababase\n",
    "    * `db: Session` is a SQLAlchemy session to interact with the database\n",
    "    * `todo: schemas.ToDoRequest` is a Pydantic object with the data of the new todo task\n",
    "    *  `db_todo` creates a new ToDo task\n",
    "    *  `db_add` adds the new ToDo task to the database Session\n",
    "    *  `db.commit` saves the changes in the database\n",
    "    *  `db.refresh` updates the database\n",
    "    *  returns the `db_todo` object\n",
    "\n",
    "* `read_todos` displays all the non-completed todo tasks\n",
    "    * if all completed, displays all the todo tasks completed\n",
    "\n",
    "* `read_todo` display the todo task identified with the id\n",
    "\n",
    "* `uddate_todo` updates the todo task idenfitied with the id\n",
    "    * if the task does not exist, it returns `None`\n",
    "    * if the tasks exists, it updates it and saves it\n",
    "      \n",
    "* `delete_todo` deletes the todo task identified with the id\n",
    "    * if the task does not exist, it returns `None`\n",
    "    * if the tasks exists, it deletes it and saves the changes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "2ab90fad-9de7-4983-aa53-07f0c2a9df0f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from sqlalchemy.orm import Session\n",
    "# import models, schemas\n",
    "\n",
    "# def create_todo(db: Session, todo: schemas.ToDoRequest):\n",
    "#     db_todo = models.ToDo(name=todo.name, completed=todo.completed)\n",
    "#     db.add(db_todo)\n",
    "#     db.commit()\n",
    "#     db.refresh(db_todo)\n",
    "#     return db_todo\n",
    "\n",
    "# def read_todos(db: Session, completed: bool):\n",
    "#     if completed is None:\n",
    "#         return db.query(models.ToDo).all()\n",
    "#     else:\n",
    "#         return db.query(models.ToDo).filter(models.ToDo.completed == completed).all()\n",
    "\n",
    "# def read_todo(db: Session, id: int):\n",
    "#     return db.query(models.ToDo).filter(models.ToDo.id == id).first()\n",
    "\n",
    "# def update_todo(db: Session, id: int, todo: schemas.ToDoRequest):\n",
    "#     db_todo = db.query(models.ToDo).filter(models.ToDo.id == id).first()\n",
    "#     if db_todo is None:\n",
    "#         return None\n",
    "#     db.query(models.ToDo).filter(models.ToDo.id == id).update({'name': todo.name, 'completed': todo.completed})\n",
    "#     db.commit()\n",
    "#     db.refresh(db_todo)\n",
    "#     return db_todo\n",
    "\n",
    "# def delete_todo(db: Session, id: int):\n",
    "#     db_todo = db.query(models.ToDo).filter(models.ToDo.id == id).first()\n",
    "#     if db_todo is None:\n",
    "#         return None\n",
    "#     db.query(models.ToDo).filter(models.ToDo.id == id).delete()\n",
    "#     db.commit()\n",
    "#     return True"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3c4d9859-094c-4532-bbe7-f7c331bc460e",
   "metadata": {},
   "source": [
    "#### Set up the router\n",
    "* Create the new file routers/todos.py\n",
    "* FastAPI API CRUD routes\n",
    "* APIRouter creates the API router with the prefix /todos\n",
    "* `get_db` opens and closes a SQLAlchemy session\n",
    "* `Depends(get_db)` injects a db session on each route\n",
    "\n",
    "* `@router.post`: route to create a new todo task\n",
    "    * `schemas.ToDoRequest` is the model that validates the data types\n",
    "    * uses the `create_todo` function defined in crud.py\n",
    "\n",
    "* `@router.get(\"\")` en `/todos`: route to display all tasks\n",
    "    * uses the `read_todos` function defined in crud.py\n",
    "    * can filter tasks based on `completed` status\n",
    " \n",
    "* `@router.get(\"/{id}\")`: route to display one task by id\n",
    "    * uses the `read_todo` function defined in crud.py\n",
    "    * if it is not found, it returns a 404 error message\n",
    "\n",
    "* `@router.put(\"/{id}\")`: route to update one task by id\n",
    "    * uses the `update_todo` function defined in crud.py\n",
    "\n",
    "* `@router.delete(\"/{id}\")`: route to display one task by id\n",
    "    * uses the `delete_todo` function defined in crud.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "cabbd9cc-756b-4bbe-ae7d-fc17bbcebb7f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# from typing import List\n",
    "# from sqlalchemy.orm import Session\n",
    "# from fastapi import APIRouter, Depends, HTTPException, status\n",
    "# import schemas\n",
    "# import crud\n",
    "# from database import SessionLocal\n",
    "\n",
    "# router = APIRouter(\n",
    "#     prefix=\"/todos\"\n",
    "# )\n",
    "\n",
    "# def get_db():\n",
    "#     db = SessionLocal()\n",
    "#     try:\n",
    "#         yield db\n",
    "#     finally:\n",
    "#         db.close()\n",
    "\n",
    "# @router.post(\"\", status_code=status.HTTP_201_CREATED)\n",
    "# def create_todo(todo: schemas.ToDoRequest, db: Session = Depends(get_db)):\n",
    "#     todo = crud.create_todo(db, todo)\n",
    "#     return todo\n",
    "\n",
    "# @router.get(\"\", response_model=List[schemas.ToDoResponse])\n",
    "# def get_todos(completed: bool = None, db: Session = Depends(get_db)):\n",
    "#     todos = crud.read_todos(db, completed)\n",
    "#     return todos\n",
    "\n",
    "# @router.get(\"/{id}\")\n",
    "# def get_todo_by_id(id: int, db: Session = Depends(get_db)):\n",
    "#     todo = crud.read_todo(db, id)\n",
    "#     if todo is None:\n",
    "#         raise HTTPException(status_code=404, detail=\"to do not found\")\n",
    "#     return todo\n",
    "\n",
    "# @router.put(\"/{id}\")\n",
    "# def update_todo(id: int, todo: schemas.ToDoRequest, db: Session = Depends(get_db)):\n",
    "#     todo = crud.update_todo(db, id, todo)\n",
    "#     if todo is None:\n",
    "#         raise HTTPException(status_code=404, detail=\"to do not found\")\n",
    "#     return todo\n",
    "\n",
    "# @router.delete(\"/{id}\", status_code=status.HTTP_200_OK)\n",
    "# def delete_todo(id: int, db: Session = Depends(get_db)):\n",
    "#     res = crud.delete_todo(db, id)\n",
    "#     if res is None:\n",
    "#         raise HTTPException(status_code=404, detail=\"to do not found\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc235a1f-9e12-4f96-a36f-63beb6bf240c",
   "metadata": {},
   "source": [
    "#### Uncomment these two lines in main.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "f565c1a7-9f74-45d5-8755-3e24265b2574",
   "metadata": {},
   "outputs": [],
   "source": [
    "#from routers import todos\n",
    "#app.include_router(todos.router)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d5c84e5-ef96-4a51-a91c-34b2e4d44beb",
   "metadata": {},
   "source": [
    "#### Run the server from the terminal"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "f80d90fc-8328-4a85-b863-9ab9df4c0581",
   "metadata": {},
   "outputs": [],
   "source": [
    "#uvicorn main:app --reload"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "739674aa-c345-4d7a-a918-4eafc327b535",
   "metadata": {},
   "source": [
    "#### In a second terminal window, make the following tests (need httpie installed)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "77d59b99-4bfa-423a-9c2a-7f0e201497ca",
   "metadata": {},
   "source": [
    "Create one to-do task:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "b6a6811c-ae5a-4bde-9fc7-2072d687623a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http POST http://localhost:8000/todos name=\"walk Fido\" completed=false"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "041c464f-fe16-4029-a2b8-43987741026c",
   "metadata": {},
   "source": [
    "Display all the to-do tasks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "de18c3df-d1bb-47e8-b9ac-be6986728507",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e16db86e-1d06-4542-8d7a-924d4f370156",
   "metadata": {},
   "source": [
    "Display the to-do task with the id=1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "id": "5da52daa-b0c6-4c86-9960-e777ee905ce0",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos/1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c742602-6d41-4319-8ddf-91e5283d1c0c",
   "metadata": {},
   "source": [
    "Update it changing completed to true:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d68733c7-2dd2-43b5-aa41-2b24b1eb0546",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http PUT http://localhost:8000/todos/1 name=\"walk Fido\" completed=true"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1bd9536-fcdc-4ed1-a958-35c12d071258",
   "metadata": {},
   "source": [
    "Display all the to-do tasks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "3d7742e7-d094-49cd-b884-f0069d295423",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "119c8df3-5a8c-4f73-b314-90b3cfb6bcd3",
   "metadata": {},
   "source": [
    "Create a second to-do task:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "04f78490-f2dc-4ccd-b720-646a80fb386f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http POST http://localhost:8000/todos name=\"feed Fido\" completed=false"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c97d04d5-538a-43e8-a110-16c2358ee859",
   "metadata": {},
   "source": [
    "Display all the to-do tasks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ee8554f1-b5c6-4fa4-8878-854bb7f923e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f4343710-936b-4b07-88ef-14d8a166c8c0",
   "metadata": {},
   "source": [
    "Delete the to-do task with the id=1:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5592f481-9a79-4923-beef-f91a52c5e828",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http DELETE http://localhost:8000/todos/1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7dcda4c5-660a-4910-831e-ca6a97a6d220",
   "metadata": {},
   "source": [
    "Display all the to-do tasks:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "540c064c-a8fd-4f62-b9e3-a96833650232",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c12ed1dc-366a-4fb1-bf54-a23124421f65",
   "metadata": {},
   "source": [
    "The following should get a \"to do not found\" message"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0be5eff4-80e5-4c70-b8a1-38a336dfbd9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "#http GET http://localhost:8000/todos/1"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0609c187-ebb8-40f5-8304-061c15b7ac45",
   "metadata": {},
   "source": [
    "## Part 2: Frontend with Next.js"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "51eb14fb-2eda-4881-b76a-1b9035930a19",
   "metadata": {},
   "source": [
    "#### Create the frontend app using a Next.js starter template"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "8fc5564c-f37d-4a7f-b892-69da28f4de58",
   "metadata": {},
   "outputs": [],
   "source": [
    "#npx create-next-app@latest"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "47eb812d-c3fc-4654-97d9-82a682d4787e",
   "metadata": {},
   "source": [
    "#### Enter this configuration options:\n",
    "* app name: todo-app\n",
    "* typescript: no\n",
    "* eslint: yes\n",
    "* tailwind css: no\n",
    "* src/ directory: no\n",
    "* app router: no\n",
    "* customize import alias: no"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a4afeaeb-dbca-448a-aad8-016dd3d1fcce",
   "metadata": {},
   "source": [
    "#### Go to the main folder of the frontend app and enter npm run dev to run the frontend app in the browser"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "171d3b40-5bcb-4324-805a-c238e73198b6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#cd todo-app\n",
    "#npm run dev"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04d71c35-0367-4553-a188-8a3444405ca1",
   "metadata": {},
   "source": [
    "#### Check how the starter template looks in your browser\n",
    "* open browser in localhost:3000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a4c9d13-e7f4-4512-b848-20f99fa9d54f",
   "metadata": {},
   "source": [
    "#### Open the starter template in your editor\n",
    "* What you will see is the UI of the default Next.js template"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d1f15e75-2c0d-44b9-b2b6-0a5c3f5a3032",
   "metadata": {},
   "source": [
    "#### Create the .env file\n",
    "* Enter the URL of the backend API\n",
    "* This is a Next.js convention: any variable starting with NEXT_PUBLIC_ will be available in the client side and in the server side."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "ddfa46c6-de1b-4a67-ad27-8ca8abb52756",
   "metadata": {},
   "outputs": [],
   "source": [
    "# NEXT_PUBLIC_API_URL=http://localhost:8000"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fb5661da-df0b-4d7e-93ec-3aea4d418d5c",
   "metadata": {},
   "source": [
    "#### Edit pages/index.js\n",
    "* remove the default content provided for this file\n",
    "* the following content imports 2 next.js components we have not created yet:\n",
    "    * Layout (we can re-use this component in any page).\n",
    "    * ToDoList (all of our TODO functionality).\n",
    "\n",
    "* The ToDoList component will be inside the Layout component. This is called \"composition\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "39d02fb2-8705-4ad0-aca0-742f949e0eb6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import Head from 'next/head'\n",
    "# import Layout from '../components/layout';\n",
    "# import ToDoList from '../components/todo-list';\n",
    "\n",
    "# export default function Home() {\n",
    "#   return (\n",
    "#     <div>\n",
    "#       <Head>\n",
    "#         <title>Full Stack Book To Do</title>\n",
    "#         <meta name=\"description\" content=\"Full Stack Book To Do\" />\n",
    "#         <link rel=\"icon\" href=\"/favicon.ico\" />\n",
    "#       </Head>\n",
    "#       <Layout>\n",
    "#         <ToDoList />\n",
    "#       </Layout>\n",
    "#     </div>\n",
    "#   )\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "48a124fa-190b-42a5-b344-911c4778973d",
   "metadata": {},
   "source": [
    "#### Create the components folder in the root directory"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fcae6151-c8c1-428e-9b4e-be6f192e6608",
   "metadata": {},
   "source": [
    "#### Create the components/layout.js file\n",
    "* Imports css styles that are not created yet\n",
    "* Defines a React component called Layout\n",
    "    * This component accepts the parameter props\n",
    "    * React components use JSX syntax, very similar to HTML\n",
    "    * The {props.children} is where we use composition, this means that we can replace this with another React component when we use the Layout component anywhere."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "20133e4b-229e-460c-a146-aee5cca67b25",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import styles from '../styles/layout.module.css'\n",
    "\n",
    "# export default function Layout(props) {\n",
    "#   return (\n",
    "#     <div className={styles.layout}>\n",
    "#       <h1 className={styles.title}>To Do</h1>\n",
    "#       {props.children}\n",
    "#     </div>\n",
    "#   )\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5c3b62f3-7b4b-4338-ab75-9698f6bb851f",
   "metadata": {},
   "source": [
    "### Create the components/todo-list.js file\n",
    "* Will require to install lodash. In terminal: npm install lodash\n",
    "* Imports css that is still not created.\n",
    "* Imports ToDo from a file not yet created."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7a53ba14-3cc7-4bb5-a798-6b61f280db96",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import styles from '../styles/todo-list.module.css'\n",
    "# import { useState, useEffect, useCallback, useRef } from 'react'\n",
    "# import { debounce } from 'lodash'\n",
    "# import ToDo from './todo'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "7295b39e-f3f0-4fcc-afb3-65fb95a21e7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "#export default function ToDoList() {"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "50112f5d-6a47-4673-b2c2-5dda44516dba",
   "metadata": {},
   "source": [
    "#### Global explanation of this component"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2187eaa0-7362-4d1d-a9e4-613f2c2eac4f",
   "metadata": {},
   "source": [
    "This code is for a React component called `ToDoList`, which is part of a web application for managing a list of tasks or \"todos\". Here's a simple breakdown of what this component does:\n",
    "\n",
    "1. **Imports and Setup**:\n",
    "   - CSS styles are imported for use in the component.\n",
    "   - Several hooks from React (`useState`, `useEffect`, `useCallback`, `useRef`) and a utility (`debounce` from `lodash`) are imported. These are used for managing state, side effects, and optimizing function calls.\n",
    "   - The `ToDo` component is also imported, which is used to render each todo item.\n",
    "\n",
    "2. **Component State Management**:\n",
    "   - `useState` is used to create several state variables: `todos` (the list of todo items), `mainInput` (the value of a main input field for adding new todos), and `filter` (to filter the displayed todos).\n",
    "   - `useRef` is used to create a ref (`didFetchRef`) for tracking if the todos have been fetched.\n",
    "\n",
    "3. **Fetching Todos on Component Mount**:\n",
    "   - `useEffect` is used to fetch the list of todos when the component first renders. It checks `didFetchRef` to ensure fetching only occurs once.\n",
    "\n",
    "4. **Functions for Handling Todos**:\n",
    "   - `fetchTodos` fetches todos from an API, possibly filtered by completion status.\n",
    "   - `debouncedUpdateTodo` is a debounced version of an `updateTodo` function, used for updating todos with a delay to improve performance.\n",
    "   - `handleToDoChange` handles changes to todo items (like marking them as completed).\n",
    "   - `updateTodo` updates a todo item in the backend.\n",
    "   - `addToDo` adds a new todo to the list.\n",
    "   - `handleDeleteToDo` deletes a todo item.\n",
    "\n",
    "5. **Handling User Input**:\n",
    "   - `handleMainInputChange` updates the `mainInput` state when the user types in the input field.\n",
    "   - `handleKeyDown` checks for the 'Enter' key to add a new todo.\n",
    "\n",
    "6. **Filtering Todos**:\n",
    "   - `handleFilterChange` updates the filter state and fetches todos based on the selected filter.\n",
    "\n",
    "7. **Rendering the Component**:\n",
    "   - The `return` statement contains the JSX (HTML-like syntax) for rendering the component.\n",
    "   - It includes an input field for adding new todos, a loading message, a list of todos (using the `ToDo` component for each item), and buttons for filtering the todos by different criteria (all, active, completed).\n",
    "\n",
    "In simple terms, the `ToDoList` component allows users to add, view, filter, and delete todo items. It interacts with an API for data fetching and updating, and it uses various React features for handling state, effects, and user interactions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06dc462e-626c-4c58-8eab-1288a1699b16",
   "metadata": {},
   "source": [
    "#### Step-by-step explanation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a1a98a5f-1438-4dc5-8002-f4859f369ff9",
   "metadata": {},
   "outputs": [],
   "source": [
    "#   const [todos, setTodos] = useState(null)\n",
    "#   const [mainInput, setMainInput] = useState('')\n",
    "#   const [filter, setFilter] = useState()\n",
    "#   const didFetchRef = useRef(false)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b840e03d-961c-46ae-a167-52d3ae731b5d",
   "metadata": {},
   "source": [
    "The previous code is a typical use of React's `useState` and `useRef` hooks in a functional component. Let's break down what each line does in simple terms:\n",
    "\n",
    "1. **`const [todos, setTodos] = useState(null)`**:\n",
    "   - This line creates a state variable named `todos` with a corresponding setter function `setTodos`.\n",
    "   - `useState(null)` initializes `todos` with a value of `null`.\n",
    "   - You can use `setTodos` to update the value of `todos` later in the component. When you do, it triggers the component to re-render with the new value.\n",
    "\n",
    "2. **`const [mainInput, setMainInput] = useState('')`**:\n",
    "   - Similarly, this line creates another state variable `mainInput` with its setter function `setMainInput`.\n",
    "   - It's initialized with an empty string `''`. This might be used, for instance, to track the value of a text input field in your UI.\n",
    "   - `setMainInput` can be used to update `mainInput`, triggering a re-render with the updated value.\n",
    "\n",
    "3. **`const [filter, setFilter] = useState()`**:\n",
    "   - This line creates a state variable `filter` with a setter `setFilter`.\n",
    "   - Since `useState()` is called without any argument, `filter` is initialized as `undefined`.\n",
    "   - `setFilter` can be used to update the value of `filter` for various purposes, like applying a filter to the list of `todos`.\n",
    "\n",
    "4. **`const didFetchRef = useRef(false)`**:\n",
    "   - This line uses the `useRef` hook to create a mutable ref object called `didFetchRef`.\n",
    "   - The ref is initialized with the value `false`. Unlike state variables, changing the value of a ref does not cause the component to re-render.\n",
    "   - This ref can be used to track whether a certain action (like data fetching) has already been performed without re-triggering renders.\n",
    "\n",
    "In summary, this code sets up two pieces of state (`todos` and `mainInput`, with `filter` being optional) to track and update component data, and uses a ref (`didFetchRef`) as a persistent variable that doesn't cause re-renders when its value changes. This pattern is common in functional components for managing both the UI state and certain actions like data fetching."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "162038ff-c1eb-4c1d-9edd-2a309b4a383f",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # useEffect(() => {\n",
    "  #   if (didFetchRef.current === false) {\n",
    "  #     didFetchRef.current = true\n",
    "  #     fetchTodos()\n",
    "  #   }\n",
    "  # }, [])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b25641e2-13b2-4474-bad6-f03385150a05",
   "metadata": {},
   "source": [
    "The previous code uses the `useEffect` hook in React, which is a way to perform side effects in functional components. Let's break down what the code does in simple terms:\n",
    "\n",
    "1. **`useEffect(() => {...}, [])`:**\n",
    "   - `useEffect` is a hook that runs some code based on certain conditions.\n",
    "   - The empty array `[]` as the second argument to `useEffect` means that the code inside the `useEffect` will run only once, right after the initial render of the component. This is similar to the `componentDidMount` lifecycle method in class components.\n",
    "\n",
    "2. **`if (didFetchRef.current === false) {...}`:**\n",
    "   - Inside `useEffect`, there's an `if` statement that checks the value of `didFetchRef.current`.\n",
    "   - `didFetchRef` is a ref created with the `useRef` hook, and it's used here to track whether some action has already been taken. In this case, it's checking whether a function to fetch data (`fetchTodos`) has been called.\n",
    "\n",
    "3. **`didFetchRef.current = true`:**\n",
    "   - If `didFetchRef.current` is `false` (meaning `fetchTodos` hasn't been called yet), the code sets `didFetchRef.current` to `true`.\n",
    "   - This is done to ensure that the data-fetching function only runs once.\n",
    "\n",
    "4. **`fetchTodos()`:**\n",
    "   - This is a call to a function named `fetchTodos`, which likely fetches data from an API or some external source.\n",
    "   - The function is called inside the `if` statement to make sure it only runs the first time the component is rendered.\n",
    "\n",
    "In simple terms, this `useEffect` hook is used to run the `fetchTodos` function only once after the component is first rendered, and `didFetchRef` is used to ensure that this function doesn't run more than once. This pattern is useful for fetching data that you only need to load once when the component initially appears on the screen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b985a2ae-fafe-4361-934b-2a3af41e54ea",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # async function fetchTodos(completed) {\n",
    "  #   let path = '/todos'\n",
    "  #   if (completed !== undefined) {\n",
    "  #     path = `/todos?completed=${completed}`\n",
    "  #   }\n",
    "  #   const res = await fetch(process.env.NEXT_PUBLIC_API_URL + path)\n",
    "  #   const json = await res.json()\n",
    "  #   setTodos(json)\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75942a12-5502-4458-96c3-a8aaa8d64ff5",
   "metadata": {},
   "source": [
    "The code snippet defines an asynchronous function named `fetchTodos` that is used for fetching a list of todo items (tasks) from an external source (like a server or an API). Here's a breakdown of what each part of the function does:\n",
    "\n",
    "1. **`async function fetchTodos(completed) {...}`:**\n",
    "   - `async` indicates that this function is asynchronous, meaning it can perform tasks that take some time to complete (like fetching data from an API) without blocking other code from running.\n",
    "   - `fetchTodos` is the name of the function.\n",
    "   - `completed` is a parameter that the function can accept. It's used to determine which todos to fetch based on their completion status.\n",
    "\n",
    "2. **Determine the API Path:**\n",
    "   - Initially, `path` is set to `'/todos'`, which seems to be the endpoint for fetching todos.\n",
    "   - If `completed` is not `undefined`, the function modifies the `path` to include a query parameter `completed`. This is likely used to filter the todos based on whether they are completed or not on the server-side.\n",
    "\n",
    "3. **Fetch the Data:**\n",
    "   - The function uses the `fetch` API to make a network request to the provided URL, which is constructed using `process.env.NEXT_PUBLIC_API_URL` (the base URL of the API) and the `path`.\n",
    "   - `await` is used to wait for the response from `fetch`. This is possible because the function is asynchronous.\n",
    "\n",
    "4. **Process the Response:**\n",
    "   - Once the response is received, `res.json()` is called to convert the response into a JSON format. Again, `await` is used to wait for this process to complete.\n",
    "   - The JSON data, presumably a list of todo items, is then stored in a variable named `json`.\n",
    "\n",
    "5. **Update State with Fetched Data:**\n",
    "   - Finally, the function updates the component's state by calling `setTodos(json)`. This updates the `todos` state with the fetched data, likely causing the component to re-render and display the new list of todos.\n",
    "\n",
    "In simple terms, `fetchTodos` is a function that fetches a list of todo items from a server and updates the component's state with these items. The function can also filter the todos based on their completion status if the `completed` parameter is provided."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5dfd5005-3081-4f0f-a9ee-7733ad7da14c",
   "metadata": {},
   "outputs": [],
   "source": [
    "# const debouncedUpdateTodo = useCallback(debounce(updateTodo, 500), [])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d113ffbb-ca8e-4d4b-8fa9-dcb31f72db21",
   "metadata": {},
   "source": [
    "The previous line of code uses two React hooks, `useCallback` and `debounce` from the `lodash` library, to create a debounced version of an `updateTodo` function. Let's break it down:\n",
    "\n",
    "1. **`updateTodo` Function**: This is presumably a function that performs some update operation, like updating a todo item. Such operations are often tied to user input or other rapid interactions.\n",
    "\n",
    "2. **`debounce(updateTodo, 500)`**:\n",
    "   - `debounce` is a function from the `lodash` library that limits the rate at which a function can fire.\n",
    "   - When you wrap `updateTodo` with `debounce`, you create a new function that will only execute `updateTodo` after it hasn't been called for 500 milliseconds.\n",
    "   - This is useful for reducing the number of times `updateTodo` is called, which can be beneficial for performance, especially if `updateTodo` involves network requests or other heavy computations.\n",
    "\n",
    "3. **`useCallback` Hook**:\n",
    "   - `useCallback` is a React hook that returns a memoized version of the callback function that only changes if one of the dependencies has changed.\n",
    "   - In this case, `useCallback` is used to memoize the debounced version of `updateTodo`. The empty dependency array `[]` means that the memoized function will only be created once and will not change on subsequent renders of the component.\n",
    "\n",
    "4. **`const debouncedUpdateTodo`**:\n",
    "   - The debounced function is stored in a constant named `debouncedUpdateTodo`. \n",
    "   - You can now use `debouncedUpdateTodo` in your component in place of `updateTodo` for operations that you want to debounce, like handling real-time input changes.\n",
    "\n",
    "In simple terms, this code creates a version of the `updateTodo` function that will only be triggered if there's a pause of 500 milliseconds between invocations, preventing it from running too frequently. This can improve performance and user experience, especially in cases of rapidly firing events like typing in a search bar or resizing a window."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "958bcd2d-5cf4-4e16-a4ee-407c0297cee3",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # function handleToDoChange(e, id) {\n",
    "  #   const target = e.target\n",
    "  #   const value = target.type === 'checkbox' ? target.checked : target.value\n",
    "  #   const name = target.name\n",
    "  #   const copy = [...todos]\n",
    "  #   const idx = todos.findIndex((todo) => todo.id === id)\n",
    "  #   const changedToDo = {\n",
    "  #     ...todos[idx],\n",
    "  #     [name]: value\n",
    "  #   }\n",
    "  #   copy[idx] = changedToDo\n",
    "  #   debouncedUpdateTodo(changedToDo)\n",
    "  #   setTodos(copy)\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "56814126-9f6a-4a3b-ad96-6d19c5b8655d",
   "metadata": {},
   "source": [
    "The previous code defines a function named `handleToDoChange` in a React component. The function is designed to handle changes to todo items (like updating a task's status or content). Let's break it down into simpler terms:\n",
    "\n",
    "1. **Function Definition**:\n",
    "   - `handleToDoChange` is a function that takes two parameters: `e` (an event object from a user interaction, like typing in a text field or checking a checkbox) and `id` (the unique identifier of a todo item).\n",
    "\n",
    "2. **Extracting Information from the Event**:\n",
    "   - `const target = e.target`: Gets the target element that triggered the event.\n",
    "   - `const value = target.type === 'checkbox' ? target.checked : target.value`: Determines the value to be updated. If the target element is a checkbox, it uses the `checked` property; otherwise, it uses the `value` property.\n",
    "   - `const name = target.name`: Gets the name of the target element, which likely corresponds to a property of the todo item (like 'completed' or 'title').\n",
    "\n",
    "3. **Finding and Updating the Todo Item**:\n",
    "   - `const copy = [...todos]`: Creates a copy of the current list of todos. This is important for immutability, a key concept in React.\n",
    "   - `const idx = todos.findIndex((todo) => todo.id === id)`: Finds the index of the todo item that needs to be updated based on its `id`.\n",
    "   - Constructs a new todo object (`changedToDo`) by copying the existing todo item and updating the property corresponding to the target element's name with the new value.\n",
    "\n",
    "4. **Updating the State**:\n",
    "   - `copy[idx] = changedToDo`: Places the updated todo item back into the correct position in the copy of the todo list.\n",
    "   - `debouncedUpdateTodo(changedToDo)`: Calls a debounced function to update the todo item, likely making changes persistent in a database or backend (this function is defined elsewhere).\n",
    "   - `setTodos(copy)`: Updates the state of the todo list with the new, modified list. This triggers a re-render of the component with the updated data.\n",
    "\n",
    "In simple terms, `handleToDoChange` is a function that updates the state of a todo item when a user interacts with it (like checking it off or editing its text). It ensures that changes are reflected both in the UI and, through `debouncedUpdateTodo`, potentially in a backend or database. The use of debouncing helps in optimizing performance, especially for frequent and rapid changes."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "458f581f-64ec-4886-baf3-a0e73094f3bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # async function updateTodo(todo) {\n",
    "  #   const data = {\n",
    "  #     name: todo.name,\n",
    "  #     completed: todo.completed\n",
    "  #   }\n",
    "  #   const res = await fetch(process.env.NEXT_PUBLIC_API_URL + `/todos/${todo.id}`, {\n",
    "  #     method: 'PUT',\n",
    "  #     body: JSON.stringify(data),\n",
    "  #     headers: {\n",
    "  #       'Content-Type': 'application/json'\n",
    "  #     }\n",
    "  #   })\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e485f20-d899-4fcb-b48c-3f3da4b39165",
   "metadata": {},
   "source": [
    "The previous code defines an asynchronous function named `updateTodo` that is used for updating a todo item in a web application. Here's a simple explanation of what it does:\n",
    "\n",
    "1. **Asynchronous Function (`async`)**:\n",
    "   - The `async` keyword indicates that the function is asynchronous, meaning it can perform operations that might take some time to complete, like sending a request to a server, without blocking other code from running.\n",
    "\n",
    "2. **Function Purpose**:\n",
    "   - The function `updateTodo` takes an argument `todo`, which is an object representing a todo item.\n",
    "\n",
    "3. **Preparing the Data for Update**:\n",
    "   - Inside the function, a new object `data` is created, containing the `name` and `completed` properties of the `todo`. This data will be sent to the server to update the corresponding todo item.\n",
    "\n",
    "4. **Making an HTTP Request**:\n",
    "   - The function then makes an HTTP request using the `fetch` function.\n",
    "   - `process.env.NEXT_PUBLIC_API_URL + `/todos/${todo.id}``: This constructs the URL for the request, combining a base API URL stored in an environment variable with the specific endpoint for updating a todo (identified by `todo.id`).\n",
    "   - The request is made using the `PUT` method, which is commonly used for updating resources in RESTful APIs.\n",
    "\n",
    "5. **Sending the Data**:\n",
    "   - `body: JSON.stringify(data)`: The data object is converted to a JSON string and included in the request body.\n",
    "   - `headers: { 'Content-Type': 'application/json' }`: The request headers indicate that the body of the request is in JSON format.\n",
    "\n",
    "6. **Awaiting the Response**:\n",
    "   - `const res = await fetch(...)`: The `await` keyword is used to wait for the server's response. The response is stored in the variable `res`, although this code doesn't do anything with `res` after receiving it.\n",
    "\n",
    "In simple terms, `updateTodo` is a function that sends an updated todo item to a server. It uses an asynchronous HTTP request to send the updated data (like the name of the todo and whether it's completed) to a specific API endpoint. The server can then process this request and update the todo item in the database or backend system."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d377f944-4a2d-4336-81e4-0e48f294a905",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # async function addToDo(name) {\n",
    "  #   const res = await fetch(process.env.NEXT_PUBLIC_API_URL + `/todos/`, {\n",
    "  #     method: 'POST',\n",
    "  #     body: JSON.stringify({\n",
    "  #       name: name,\n",
    "  #       completed: false\n",
    "  #     }),\n",
    "  #     headers: {\n",
    "  #       'Content-Type': 'application/json'\n",
    "  #     }\n",
    "  #   })\n",
    "  #   if (res.ok) {\n",
    "  #     const json = await res.json();\n",
    "  #     const copy = [...todos, json]\n",
    "  #     setTodos(copy)\n",
    "  #   }\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1e06d12-be12-4b0b-b8f6-0c90ce8686be",
   "metadata": {},
   "source": [
    "The previous code defines an asynchronous function named `addToDo` in a web application, typically one built with React. The function is designed to add a new todo item to a list. Here's a simple explanation of each part:\n",
    "\n",
    "1. **Asynchronous Function**:\n",
    "   - `async function addToDo(name) {...}`: This is an asynchronous function named `addToDo`. It takes one argument, `name`, which represents the name of the todo item to be added.\n",
    "   - Being asynchronous means it can perform tasks like network requests without blocking other operations.\n",
    "\n",
    "2. **Making a Network Request**:\n",
    "   - The function sends a network request to a server using the `fetch` API.\n",
    "   - The URL for the request is constructed from an environment variable (`process.env.NEXT_PUBLIC_API_URL`) and the endpoint path `/todos/`. This is where the todo item will be sent.\n",
    "   - It uses the `POST` method, which is commonly used for sending data to create a new resource (in this case, a new todo item).\n",
    "\n",
    "3. **Sending Data**:\n",
    "   - The `body` of the request is a JSON string containing the new todo item data. It includes the `name` of the todo and a `completed` status, which is initially set to `false`.\n",
    "   - The `headers` specify that the content being sent is in JSON format.\n",
    "\n",
    "4. **Handling the Server Response**:\n",
    "   - `await` is used to wait for the response from the server. The response is stored in the variable `res`.\n",
    "   - If the response is successful (`res.ok`), the function proceeds to process the response.\n",
    "   - `await res.json()` converts the response body to a JavaScript object, stored in `json`. This object likely represents the newly created todo item.\n",
    "\n",
    "5. **Updating the Todo List**:\n",
    "   - The function creates a new array `copy` by spreading the current `todos` array and adding the new todo item (`json`) to the end.\n",
    "   - `setTodos(copy)` is called to update the state of the todo list with this new array. This will cause the component to re-render and display the updated list, including the newly added todo.\n",
    "\n",
    "In simple terms, the `addToDo` function adds a new todo item to a list by sending it to a server and then updating the local state to reflect this addition. This is a common pattern in React applications for handling user input and synchronizing with a backend server."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "50462c32-020e-40f2-8ec3-77d462e8c717",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # async function handleDeleteToDo(id) {\n",
    "  #   const res = await fetch(process.env.NEXT_PUBLIC_API_URL + `/todos/${id}`, {\n",
    "  #     method: 'DELETE',\n",
    "  #     headers: {\n",
    "  #       'Content-Type': 'application/json'\n",
    "  #     }\n",
    "  #   })\n",
    "  #   if (res.ok) {\n",
    "  #     const idx = todos.findIndex((todo) => todo.id === id)\n",
    "  #     const copy = [...todos]\n",
    "  #     copy.splice(idx, 1)\n",
    "  #     setTodos(copy)\n",
    "  #   }\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0fecca41-9375-47bc-8e80-a992e87156e4",
   "metadata": {},
   "source": [
    "This code defines an asynchronous function named `handleDeleteToDo` in a React application. The function is used to delete a todo item from a list. Let's break down what it does in simpler terms:\n",
    "\n",
    "1. **Asynchronous Function**:\n",
    "   - `async function handleDeleteToDo(id) {...}`: This is an asynchronous function, meaning it can perform tasks that take some time to complete (like sending a request to a server) without blocking other code from running.\n",
    "   - The function takes one parameter, `id`, which is likely the unique identifier of the todo item that needs to be deleted.\n",
    "\n",
    "2. **Sending a Network Request**:\n",
    "   - The function sends a network request to a server using the `fetch` API.\n",
    "   - The URL for the request is constructed from an environment variable (`process.env.NEXT_PUBLIC_API_URL`) and the specific endpoint for the todo item (`/todos/${id}`). This endpoint is used to specify which todo item should be deleted.\n",
    "   - It uses the `DELETE` method, which is commonly used in web APIs to remove a resource (in this case, a todo item).\n",
    "\n",
    "3. **Setting Request Headers**:\n",
    "   - The headers indicate that the content type of the request is JSON. This is part of the HTTP protocol and helps the server understand the format of the request.\n",
    "\n",
    "4. **Handling the Server Response**:\n",
    "   - `await` is used to wait for the server's response. The response is stored in the variable `res`.\n",
    "   - If the response is successful (`res.ok`), the function proceeds to update the local state.\n",
    "\n",
    "5. **Updating the Local State**:\n",
    "   - The function finds the index (`idx`) of the todo item to be deleted from the current list of todos (`todos`).\n",
    "   - It then creates a copy of the todos array and removes the item at the found index using `splice(idx, 1)`.\n",
    "   - Finally, `setTodos(copy)` is called to update the state with the new array of todos, which no longer includes the deleted item. This triggers a re-render of the component with the updated todo list.\n",
    "\n",
    "In simple terms, the `handleDeleteToDo` function deletes a specific todo item by sending a request to a server, and if successful, it updates the local list of todos to reflect this deletion. This pattern is common in React applications for handling data synchronization with a backend server while keeping the UI up-to-date."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27f55554-4b0d-4120-a7d1-3799eb5f009b",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # function handleMainInputChange(e) {\n",
    "  #   setMainInput(e.target.value)\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db014131-9ceb-4c05-a5db-0d9274559027",
   "metadata": {},
   "source": [
    "The previous code defines a function named `handleMainInputChange` in a React component. The function is designed to handle changes in a text input field. Here's a simple explanation:\n",
    "\n",
    "1. **Function Definition**:\n",
    "   - `function handleMainInputChange(e) {...}`: This is a function that gets called whenever there is a change in a specific input field in your component.\n",
    "   - It takes one parameter, `e`, which represents the event object that contains information about the change event.\n",
    "\n",
    "2. **Updating Component State**:\n",
    "   - `setMainInput(e.target.value)`: This line updates the state of the component.\n",
    "   - `e.target` refers to the DOM element that triggered the event (in this case, the input field).\n",
    "   - `e.target.value` is the current value of that input field after the change.\n",
    "   - `setMainInput` is a function that updates the state variable `mainInput` with the new value of the input field.\n",
    "   - This state update will cause the component to re-render if `mainInput` is used in the render output.\n",
    "\n",
    "In simple terms, `handleMainInputChange` is a function that updates the `mainInput` state of the component with the new value from a text input field every time the user types or changes the content in that field. This is a common pattern in React for handling user input and keeping the component's state in sync with the UI."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2c38ff6c-c8e1-4983-bf7d-dfbc4bda5bc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # function handleKeyDown(e) {\n",
    "  #   if (e.key === 'Enter') {\n",
    "  #     if (mainInput.length > 0) {\n",
    "  #       addToDo(mainInput)\n",
    "  #       setMainInput('')\n",
    "  #     }\n",
    "  #   }\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "21caca4f-a941-453d-967b-a7078e9684c1",
   "metadata": {},
   "source": [
    "The previous code defines a function named `handleKeyDown` in a React component. The function is designed to respond to keyboard events, specifically when a key is pressed. Let's break it down:\n",
    "\n",
    "1. **Function Definition**:\n",
    "   - `function handleKeyDown(e) {...}`: This is a function that gets called whenever a key is pressed in an element that this function is attached to (like an input field).\n",
    "   - The parameter `e` represents the event object that contains information about the key press event.\n",
    "\n",
    "2. **Checking the Pressed Key**:\n",
    "   - `if (e.key === 'Enter') {...}`: This line checks if the key pressed is the 'Enter' key. The `key` property of the event object holds the value of the key that was pressed.\n",
    "\n",
    "3. **Conditionally Adding a Todo Item**:\n",
    "   - `if (mainInput.length > 0) {...}`: This condition checks if the `mainInput` (presumably a state variable tracking the current value of an input field) is not empty.\n",
    "   - If `mainInput` is not empty and the 'Enter' key was pressed, the function then proceeds to execute two actions:\n",
    "     - `addToDo(mainInput)`: This calls a function `addToDo` and passes `mainInput` as an argument. It's likely that `addToDo` is a function for adding a new todo item with the text from `mainInput`.\n",
    "     - `setMainInput('')`: This resets the `mainInput` state to an empty string, clearing the input field. This is typically done after the input value has been used (like after adding the todo item).\n",
    "\n",
    "In simple terms, the `handleKeyDown` function is designed to listen for a key press event, and when the 'Enter' key is pressed, it checks if there is any text in the `mainInput`. If there is, it adds a new todo item with this text and then clears the input field. This kind of functionality is common in todo list applications, allowing users to add items to their list by typing text into an input field and pressing 'Enter'."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c7f862f6-c191-4675-af02-01a807d18ff5",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # function handleFilterChange(value) {\n",
    "  #   setFilter(value)\n",
    "  #   fetchTodos(value)\n",
    "  # }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "470d3523-f54d-45db-b1fd-59ddc05cc900",
   "metadata": {},
   "source": [
    "The previous code defines a function named `handleFilterChange` in a React component. The function is designed to handle changes in the way todo items are filtered. Let's simplify each part:\n",
    "\n",
    "1. **Function Definition**:\n",
    "   - `function handleFilterChange(value) {...}`: This is a function that likely gets called when a user interacts with some sort of filter control, like buttons or a dropdown menu.\n",
    "   - The parameter `value` represents the new filter option selected by the user.\n",
    "\n",
    "2. **Updating the Filter State**:\n",
    "   - `setFilter(value)`: This line updates the component's state for the filter criteria.\n",
    "   - `setFilter` is a function that changes the state variable `filter` to the new value provided. This probably controls what todos are currently being shown based on the filter (like 'all', 'completed', 'active').\n",
    "\n",
    "3. **Fetching Filtered Todos**:\n",
    "   - `fetchTodos(value)`: After updating the filter state, the function calls `fetchTodos`, passing the new filter value.\n",
    "   - `fetchTodos` is likely a function that fetches the todo items based on the selected filter criteria. For example, it could make a network request to a server to get all todos, only completed todos, or only active todos, depending on the filter value.\n",
    "\n",
    "In simple terms, `handleFilterChange` is a function that updates which todo items are displayed based on a selected filter. It does this by updating the filter state and then fetching the todo items that match this new filter criteria. This is a common pattern in applications where you need to display different subsets of data based on user selection or interaction."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bce06019-6e9d-4a27-bdbc-8c8b7f9f8016",
   "metadata": {},
   "outputs": [],
   "source": [
    "  # return (\n",
    "  #   <div className={styles.container}>\n",
    "  #     <div className={styles.mainInputContainer}>\n",
    "  #       <input className={styles.mainInput} placeholder=\"What needs to be done?\" value={mainInput} onChange={(e) => handleMainInputChange(e)} onKeyDown={handleKeyDown}></input>\n",
    "  #     </div>\n",
    "  #     {!todos && (\n",
    "  #       <div>Loading...</div>\n",
    "  #     )}\n",
    "  #     {todos && (\n",
    "  #       <div>\n",
    "  #         {todos.map((todo) => {\n",
    "  #           return (\n",
    "  #             <ToDo key={todo.id} todo={todo} onDelete={handleDeleteToDo} onChange={handleToDoChange} />\n",
    "  #           )\n",
    "  #         })}\n",
    "  #       </div>\n",
    "  #     )}\n",
    "  #     <div className={styles.filters}>\n",
    "  #       <button className={`${styles.filterBtn} ${filter === undefined && styles.filterActive}`} onClick={() => handleFilterChange()}>All</button>\n",
    "  #       <button className={`${styles.filterBtn} ${filter === false && styles.filterActive}`} onClick={() => handleFilterChange(false)}>Active</button>\n",
    "  #       <button className={`${styles.filterBtn} ${filter === true && styles.filterActive}`} onClick={() => handleFilterChange(true)}>Completed</button>\n",
    "  #     </div>\n",
    "  #   </div>\n",
    "  # )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0a2d5b8e-a5fb-4bf1-a004-713ae930bbea",
   "metadata": {},
   "source": [
    "The previous code is the part of a React component (`ToDoList`) that describes what the component should render on the screen, also known as the JSX return block. Here's a simple explanation of each part:\n",
    "\n",
    "1. **Container Div**:\n",
    "   - The entire component is wrapped in a `<div>` element with a `className` of `styles.container`, applying CSS styles to the overall layout.\n",
    "\n",
    "2. **Input for Adding Todos**:\n",
    "   - Inside the container, there is a `<div>` for the main input field, styled with `styles.mainInputContainer`.\n",
    "   - An `<input>` element is used for entering new todo items. It has:\n",
    "     - A `placeholder` text \"What needs to be done?\" to guide the user.\n",
    "     - Its value is tied to the `mainInput` state variable, so it displays what's currently in `mainInput`.\n",
    "     - Event handlers `onChange` and `onKeyDown` are attached to it. These call `handleMainInputChange` when the input changes and `handleKeyDown` when a key is pressed, respectively.\n",
    "\n",
    "3. **Loading Message**:\n",
    "   - `{!todos && <div>Loading...</div>}`: This line checks if `todos` is not yet available (like it's `null` or `undefined`). If so, it displays a \"Loading...\" message. This is likely a placeholder while the todos are being fetched from an API.\n",
    "\n",
    "4. **Displaying Todos**:\n",
    "   - `{todos && <div>...</div>}`: This checks if `todos` is available. If it is, it goes through each todo item and renders it.\n",
    "   - `todos.map(...)` is a JavaScript function that iterates over each item in the `todos` array.\n",
    "   - For each `todo` item, it renders a `ToDo` component, passing the todo item, and functions `handleDeleteToDo` and `handleToDoChange` as props. This means each todo item will have its own display and functionality, like being able to be deleted or changed.\n",
    "\n",
    "5. **Filter Buttons**:\n",
    "   - There are three buttons for filtering the displayed todos: All, Active, and Completed.\n",
    "   - Each button has an `onClick` handler that calls `handleFilterChange` with different values (`undefined`, `false`, `true`) to change the current filter.\n",
    "   - The buttons are styled, and their style changes based on the current filter value to visually indicate which filter is active.\n",
    "\n",
    "In simple terms, this part of the `ToDoList` component creates the user interface for the todo list application. It includes an input field for adding new todos, displays a loading message if the todos are still being fetched, shows the list of todos once they are available, and provides buttons to filter the displayed todos."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f570890e-1524-4d02-829f-ac01b49ddadf",
   "metadata": {},
   "source": [
    "## Create the /components/todo.js file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40aa7478-844b-42a8-96b3-e9b35329236b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# import Image from 'next/image'\n",
    "# import styles from '../styles/todo.module.css'\n",
    "\n",
    "# export default function ToDo(props) {\n",
    "#   const { todo, onChange, onDelete } = props;\n",
    "#   return (\n",
    "#     <div className={styles.toDoRow} key={todo.id}>\n",
    "#       <input className={styles.toDoCheckbox} name=\"completed\" type=\"checkbox\" checked={todo.completed} value={todo.completed} onChange={(e) => onChange(e, todo.id)}></input>\n",
    "#       <input className={styles.todoInput} autoComplete='off' name=\"name\" type=\"text\" value={todo.name} onChange={(e) => onChange(e, todo.id)}></input>\n",
    "#       <button className={styles.deleteBtn} onClick={() => onDelete(todo.id)}><Image src=\"/delete-outline.svg\" width=\"24\" height=\"24\" /></button>\n",
    "#     </div>\n",
    "#   )\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "93cf4414-9595-4e17-8a0f-e15d1b1b59d5",
   "metadata": {},
   "source": [
    "The previous code is for a React component called `ToDo`, typically used in a todo list application built with Next.js. The component is designed to display and interact with a single todo item. Here's a breakdown of the code in simple terms:\n",
    "\n",
    "1. **Imports**:\n",
    "   - `Image` from 'next/image': This is a Next.js optimized image component that allows for efficient image loading.\n",
    "   - `styles` from a CSS module: This imports specific CSS styles for the component.\n",
    "\n",
    "2. **Component Function `ToDo`**:\n",
    "   - `export default function ToDo(props) {...}`: This defines the `ToDo` component. It is a functional component that takes `props` as its argument.\n",
    "   - `const { todo, onChange, onDelete } = props;`: This line extracts the `todo`, `onChange`, and `onDelete` properties from `props`. These are likely passed from the parent component and contain the todo item data and functions for handling changes and deletion.\n",
    "\n",
    "3. **Rendering the Todo Item**:\n",
    "   - The component returns JSX (a syntax extension for JavaScript used with React) that describes the structure of the UI for the todo item.\n",
    "   - `<div className={styles.toDoRow} key={todo.id}>`: This `div` acts as a container for the todo item. It uses styles from the imported CSS module and a unique `key` based on the todo's `id`.\n",
    "\n",
    "4. **Todo Checkbox**:\n",
    "   - `<input className={styles.toDoCheckbox} ...>`: This is a checkbox input that allows marking the todo item as completed or not. \n",
    "   - `checked={todo.completed}`: The checkbox is checked or unchecked based on the `completed` property of the `todo` object.\n",
    "   - `onChange={(e) => onChange(e, todo.id)}`: This sets up a handler so that when the checkbox changes, the `onChange` function is called with the event `e` and the `id` of the todo.\n",
    "\n",
    "5. **Todo Text Input**:\n",
    "   - `<input className={styles.todoInput} ...>`: This is a text input field for the todo item's name.\n",
    "   - `value={todo.name}`: The input displays the name of the todo.\n",
    "   - The `onChange` handler here works similarly to the checkbox, allowing the name of the todo to be edited.\n",
    "\n",
    "6. **Delete Button**:\n",
    "   - `<button className={styles.deleteBtn} ...>`: This is a button for deleting the todo item.\n",
    "   - `onClick={() => onDelete(todo.id)}`: When the button is clicked, the `onDelete` function is called with the `id` of the todo.\n",
    "   - The button includes an `<Image>` component, which displays an icon from a provided source (`/material-symbols_delete-outline-sharp.svg`). The `width` and `height` are set for the image.\n",
    "\n",
    "In simple terms, the `ToDo` component is a part of a user interface for a todo list application. It displays each todo item with a checkbox to mark it as complete, an editable text field for the todo name, and a delete button with an image icon. The component allows for interaction with the todo item, including changing its completion status, editing its name, and removing it from the list."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1f66379e-4729-4d98-904b-e63cecbd5ebb",
   "metadata": {},
   "source": [
    "## Create the style files"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "14acb0d4-9921-411c-9444-b81f96a09260",
   "metadata": {},
   "source": [
    "#### Delete the content in the default css files:\n",
    "* globals.css\n",
    "* Home.module.css"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1621f75c-1d96-406d-b4ad-dabe4301ccad",
   "metadata": {},
   "source": [
    "#### styles/layout.module.css"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "36ea71eb-8f59-4dcf-b6c1-c6469fda3a91",
   "metadata": {},
   "outputs": [],
   "source": [
    "# .layout {\n",
    "#     width: 300px;\n",
    "#     margin: 20px;\n",
    "# }\n",
    "\n",
    "# .title {\n",
    "#     text-align: center;\n",
    "#     font-size: 24px;\n",
    "#     margin: 10px;\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b4caead0-a4eb-40ad-ab29-136a8b1320d5",
   "metadata": {},
   "source": [
    "#### styles/todo-list.module.css"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "87b8eb7b-44da-43ed-b7a0-9ca51599f6c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# .container {\n",
    "#   width: 300px;\n",
    "#   border: 1px solid black;\n",
    "# }\n",
    "\n",
    "# .mainInputContainer {\n",
    "#   width: 100%;\n",
    "#   margin: 20px 0;\n",
    "# }\n",
    "\n",
    "# .mainInput {\n",
    "#   padding: 5px;\n",
    "#   border: 1px solid black;\n",
    "#   margin: auto;\n",
    "#   display: block;\n",
    "#   width: 260px;\n",
    "#   height: 40px;\n",
    "# }\n",
    "\n",
    "# .filters {\n",
    "#   display: flex;\n",
    "#   justify-content: space-between;\n",
    "#   padding: 20px;\n",
    "#   margin-top: 20px;\n",
    "#   border-top: 1px solid black;\n",
    "# }\n",
    "\n",
    "# .filterBtn {\n",
    "#   background: none;\n",
    "#   border: none;\n",
    "#   cursor: pointer;\n",
    "# }\n",
    "\n",
    "# .filterActive {\n",
    "#   text-decoration: underline;\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6f3f0a3-18e7-4694-8c2c-6309bdca5915",
   "metadata": {},
   "source": [
    "#### styles/todo.module.css"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b0c63e1e-722d-4850-ba89-b200e498793e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# .container {\n",
    "#   width: 300px;\n",
    "#   border: 1px solid black;\n",
    "# }\n",
    "\n",
    "# .mainInputContainer {\n",
    "#   width: 100%;\n",
    "#   margin: 20px 0;\n",
    "# }\n",
    "\n",
    "# .mainInput {\n",
    "#   padding: 5px;\n",
    "#   border: 1px solid black;\n",
    "#   margin: auto;\n",
    "#   display: block;\n",
    "#   width: 260px;\n",
    "#   height: 40px;\n",
    "# }\n",
    "\n",
    "# .filters {\n",
    "#   display: flex;\n",
    "#   justify-content: space-between;\n",
    "#   padding: 20px;\n",
    "#   margin-top: 20px;\n",
    "#   border-top: 1px solid black;\n",
    "# }\n",
    "\n",
    "# .filterBtn {\n",
    "#   background: none;\n",
    "#   border: none;\n",
    "#   cursor: pointer;\n",
    "# }\n",
    "\n",
    "# .filterActive {\n",
    "#   text-decoration: underline;\n",
    "# }"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c51e651d-bbfd-4922-882d-f22e6fedbbc0",
   "metadata": {},
   "source": [
    "## Load the delete icon in the public folder\n",
    "* You can download it here: [https://iconduck.com/icons/28730/delete-outline](https://iconduck.com/icons/28730/delete-outline)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e23a8674-b5e8-4fb0-af3c-3ac8b84a2155",
   "metadata": {},
   "source": [
    "## Part 3: Run the full-stack app"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d58c67b9-fea3-46ca-9444-d3a8173c7f59",
   "metadata": {},
   "source": [
    "#### You will need to have the backend app open from another terminal window"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "eec08577-11de-4fc4-bb0d-f7fb95bba4a8",
   "metadata": {},
   "outputs": [],
   "source": [
    "#uvicorn main:app --reload"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c70a071-eac1-4218-8d63-2c0e359306ca",
   "metadata": {},
   "source": [
    "#### Open an additional terminal window an run the frontend app"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4adf64e3-f9d2-4c91-9d8a-917108c13189",
   "metadata": {},
   "outputs": [],
   "source": [
    "#npm run dev"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6f0c7779-c596-4d21-9fc1-fe6c5e15bb75",
   "metadata": {},
   "source": [
    "#### If you are using the Chrome browser, you can open DevTools and see the operations that are happening in the background when you make changes in the todo app tasks."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "fa2778dc-3486-4208-a854-62b8583d131d",
   "metadata": {},
   "source": [
    "## Part 4: Upload backend to Github"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd5e0694-d7bd-4118-bfe1-bc0bb2565473",
   "metadata": {},
   "source": [
    "Uploading your backend application to a GitHub account is a relatively straightforward process. Here's a step-by-step guide to do it:\n",
    "\n",
    "### Step 1: Create a Repository on GitHub\n",
    "\n",
    "1. **Log in to GitHub**: Go to [GitHub](https://github.com/) and make sure you're registered or log in.\n",
    "\n",
    "2. **Create a New Repository**:\n",
    "   - Click on the \"+\" icon in the top right corner and select \"New repository\".\n",
    "   - Name your repository, add a description (optional), and choose whether it should be public or private.\n",
    "   - You can also initialize the repository with a README file, a license, or a `.gitignore`, although this is optional and can be done later.\n",
    "\n",
    "### Step 2: Prepare Your Local Project\n",
    "\n",
    "1. **Organize Your Code Locally**:\n",
    "   - If you haven't already, organize your code in a folder on your computer. Ensure everything you need is included and that confidential files (like `.env` with credentials) are excluded or listed in `.gitignore`.\n",
    "\n",
    "2. **Initialize Git in Your Project** (if not already done):\n",
    "   - Open a terminal or command line.\n",
    "   - Navigate (`cd`) to your project folder.\n",
    "   - Run `git init` to initialize a new Git repository.\n",
    "\n",
    "3. **Add a `.gitignore` File** (if you don't have one):\n",
    "   - Create a `.gitignore` file at the root of your project.\n",
    "   - Add names of files or folders you don't want to upload to GitHub (for example, `node_modules`, sensitive configuration files, etc.).\n",
    "\n",
    "### Step 3: Upload Your Code to GitHub\n",
    "\n",
    "1. **Add Files to the Local Git Repository**:\n",
    "   - From the terminal, in your project folder, run `git add .` to add all files to the repository (respecting `.gitignore`).\n",
    "   - Or use `git add [file]` to add specific files.\n",
    "\n",
    "2. **Make Your First Commit**:\n",
    "   - Run `git commit -m \"First commit\"` to make the first commit with a descriptive message.\n",
    "\n",
    "3. **Link Your Local Repository with GitHub**:\n",
    "   - On GitHub, on your repository page, you'll find a URL for the repository. It will be something like `https://github.com/your-user/your-repository.git`.\n",
    "   - In your terminal, run `git remote add origin [repository URL]` to link your local repository with GitHub.\n",
    "\n",
    "4. **Push Your Code to GitHub**:\n",
    "   - Run `git push -u origin master` (or `main` if your main branch is called `main`) to push your code to the GitHub repository.\n",
    "\n",
    "### Step 4: Verify and Continue Development\n",
    "\n",
    "- **Verify on GitHub**: After uploading your code, go to your repository page on GitHub to make sure everything is there.\n",
    "- **Future Development**: For future commits, you only need to do `git add`, `git commit`, and `git push`.\n",
    "\n",
    "And with that, your backend application should be on GitHub. Always remember to keep sensitive data secure and use good Git practices for managing your code."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7bdff1d2-845d-42ee-9f62-5a752438acd2",
   "metadata": {},
   "source": [
    "## Part 5: Deploy backend to Render.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b61b2d67-714d-4cca-9e16-e6d01115766d",
   "metadata": {},
   "source": [
    "Deploying your FastAPI backend with a PostgreSQL database on Render.com involves several steps. Render offers a fairly straightforward solution for deploying web applications and databases. Here is a basic guide to do it:\n",
    "\n",
    "### Step 1: Prepare Your FastAPI Application\n",
    "\n",
    "Before deploying, make sure your FastAPI application is production-ready. This includes:\n",
    "\n",
    "1. **Check Dependencies**: Ensure all necessary dependencies are listed in a `requirements.txt` file.\n",
    "\n",
    "2. **Application Configuration**: Verify that your application is configured to use environment variables for important configurations, such as database credentials.\n",
    "\n",
    "3. **Dockerfile (Optional)**: If you prefer to deploy using Docker, make sure you have a suitable `Dockerfile` for your application. Render supports deployments both with and without Docker.\n",
    "\n",
    "### Step 2: Set Up Your PostgreSQL Database\n",
    "\n",
    "1. **Create a Database on Render**:\n",
    "   - Go to the Render dashboard and create a new PostgreSQL database service.\n",
    "   - Render will provide the database credentials, including the hostname, port, username, password, and database name.\n",
    "\n",
    "2. **Configure Environment Variables**:\n",
    "   - Note down the database credentials, as you will need them to configure your FastAPI application.\n",
    "\n",
    "### Step 3: Deploy Your FastAPI Application\n",
    "\n",
    "1. **Create a New Web Service on Render**:\n",
    "   - In the Render dashboard, choose the option to create a new web service.\n",
    "   - Select the repository where your FastAPI code is.\n",
    "   - Configure the deployment options, such as the runtime environment (if you are not using Docker).\n",
    "\n",
    "2. **Set Environment Variables for FastAPI**:\n",
    "   - In your web service settings on Render, set the necessary environment variables for your application, including the PostgreSQL database credentials.\n",
    "\n",
    "3. **Deployment**:\n",
    "   - Render will start the deployment process once you have configured your service and saved the changes.\n",
    "   - If you have set up everything correctly, Render will build and deploy your application.\n",
    "\n",
    "4. **Review and Testing**:\n",
    "   - After deploying, be sure to review the available logs in Render to verify that everything is working as expected.\n",
    "   - Perform tests to ensure that your FastAPI application is communicating correctly with the PostgreSQL database.\n",
    "\n",
    "### Step 4: Updates and Maintenance\n",
    "\n",
    "- **Updates**: To update your application, simply push your changes to the repository connected to Render. Render will automatically initiate a new deployment.\n",
    "- **Monitor Your Application**: Use Render's tools to monitor the performance and health of your application and database.\n",
    "\n",
    "### Final Considerations\n",
    "\n",
    "- **Security**: Ensure that your application and database are configured securely.\n",
    "- **Database Backups**: Set up regular backups for your database on Render to prevent data loss.\n",
    "\n",
    "Render.com greatly facilitates the process of deploying applications and databases, integrating well with code repositories and providing a manageable platform for deployment and application management."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "26eae2ea-fbb9-404e-a081-6a3853067553",
   "metadata": {},
   "source": [
    "## How to add environment variables in Render.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "794b3687-88b5-421d-9b7e-fc2bd5b975b6",
   "metadata": {},
   "source": [
    "To set up environment variables for your FastAPI application on Render.com, follow these steps:\n",
    "\n",
    "### Access Your Web Service Settings on Render\n",
    "\n",
    "1. **Log in to Render**: Go to [Render.com](https://render.com/) and log in to your account.\n",
    "\n",
    "2. **Navigate to Your Web Service**: In the Render dashboard, locate the web service you created for your FastAPI application.\n",
    "\n",
    "3. **Enter the Service Configuration**: Click on the web service to open its configuration panel.\n",
    "\n",
    "### Set Up the Environment Variables\n",
    "\n",
    "1. **Find the Environment Variables Section**: Within the service configuration, look for a section called \"Environment Variables\" or something similar.\n",
    "\n",
    "2. **Add New Environment Variables**:\n",
    "   - Click the button to add a new environment variable.\n",
    "   - Enter the name and value for each required variable.\n",
    "   - For example, if your FastAPI application uses environment variables for database connection, you will need to add variables such as `DATABASE_URL`, `DATABASE_USER`, `DATABASE_PASSWORD`, etc., with the corresponding values you obtained when setting up your PostgreSQL database in Render.\n",
    "\n",
    "### Common Examples of Environment Variables\n",
    "\n",
    "- **`DATABASE_URL`**: The full URL to connect to your PostgreSQL database.\n",
    "- **`DATABASE_USER` and `DATABASE_PASSWORD`**: Username and password for the database.\n",
    "- **Application Configuration Variables**: Any other variables your application needs for its configuration, such as secret keys, operation modes, etc.\n",
    "\n",
    "### Save and Apply Changes\n",
    "\n",
    "- After adding all your environment variables, make sure to save the changes.\n",
    "- Render might automatically restart your service to apply these changes. If not, you can manually restart the service to ensure the new environment variables are in use.\n",
    "\n",
    "### Verify Everything Works\n",
    "\n",
    "- Once your service restarts with the new variables, verify that your application is running correctly and can connect to the database using the configured environment variables.\n",
    "\n",
    "Properly configuring environment variables is crucial for the security and correct functioning of your application in production. These variables allow your application to access important resources such as databases and external APIs, maintaining the sensitivity and configurability of these details."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "db072e29-18c4-4b82-975e-19efa2053931",
   "metadata": {},
   "source": [
    "## Create the todos table in the postgres database"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3f42cc16-a43c-4720-86dc-6ce2cb258509",
   "metadata": {},
   "source": [
    "Edit the remote postgress database hosted in Render.com from your terminal:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "9d5452e4-3ee0-4d68-aceb-58f291f8499a",
   "metadata": {},
   "outputs": [],
   "source": [
    "#psql postgresql://user:password@host:port/databasename"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "faa6f45f-f4a5-4061-b50b-a3f01ef784ef",
   "metadata": {},
   "outputs": [],
   "source": [
    "# CREATE TABLE todos (\n",
    "#     id BIGSERIAL PRIMARY KEY,\n",
    "#     name TEXT,\n",
    "#     completed BOOLEAN NOT NULL DEFAULT false\n",
    "# );"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a5504806-6b2c-4501-92d6-eced90d417a6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#\\dt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3693d637-ed24-4ddf-8c9d-4ac92777e8c7",
   "metadata": {},
   "outputs": [],
   "source": [
    "#\\q"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c8aa14d6-dc48-414a-8c2c-85f1771acf71",
   "metadata": {},
   "source": [
    "## How to verify the backend is running correctly on Render.com"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "43b2ee97-75e7-4840-baf2-bfd32df75b66",
   "metadata": {},
   "source": [
    "To verify that your application is running correctly and can connect to the database using the configured environment variables, you can follow these steps:\n",
    "\n",
    "### 1. Check the Application Logs\n",
    "\n",
    "After deploying your application and setting up the environment variables, the first thing to do is check the application's logs:\n",
    "\n",
    "- On Render.com, go to the dashboard of your web service.\n",
    "- Look for a logs or records section. This will show you the output of your application, including startup messages and errors.\n",
    "- Check these logs for errors or messages related to the database connection. If your application cannot connect to the database, you will likely see errors here.\n",
    "\n",
    "### 2. Perform Connectivity Tests\n",
    "\n",
    "If your FastAPI application exposes endpoints that perform read/write operations on the database, you can test these endpoints to ensure that the database connection is working properly:\n",
    "\n",
    "- Use a tool like Postman or simply a browser to make requests to your API endpoints that require access to the database.\n",
    "- Observe if the operations are completed successfully (for example, reading data, creating new records, updating or deleting existing records).\n",
    "\n",
    "### 3. Verify Application Behavior\n",
    "\n",
    "If your application has a user interface (frontend):\n",
    "\n",
    "- Interact with the application as a normal user would.\n",
    "- Perform actions that you know depend on the database and observe if they behave as expected.\n",
    "\n",
    "### 4. Check Security Configurations\n",
    "\n",
    "- Make sure that your database's security configurations allow connections from your application deployed on Render. This may include setting up allowed IPs or adjusting firewall rules.\n",
    "\n",
    "### 5. Use Diagnostic Tools\n",
    "\n",
    "- If you have access to database diagnostic or monitoring tools (like those provided by Render's database service or external tools), use them to verify if there are active connections and if queries are being executed.\n",
    "\n",
    "### 6. Consult Documentation and Support\n",
    "\n",
    "- If you encounter problems, consult the documentation of Render and FastAPI to see if there are specific configuration or troubleshooting steps you might have overlooked.\n",
    "- If problems persist, consider seeking help in community forums or Render's technical support.\n",
    "\n",
    "### 7. Verify Environment Variables\n",
    "\n",
    "- Make sure the environment variables are correctly configured in Render and that your application is using them as expected.\n",
    "\n",
    "By following these steps, you should get a good idea of whether your application is running correctly and if it's connecting to the database as expected."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0fddd63-1a07-412a-98b7-d9be93b27311",
   "metadata": {},
   "source": [
    "## Part 6: Load the frontend to Github\n",
    "* Follow the process explained in Part 4."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc8b7b32-47b9-4f79-9cd1-244beb17e8d6",
   "metadata": {},
   "source": [
    "## Part 7: Deploy the frontend to Vercel"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3159215b-58c4-432a-8875-6cedce0cd391",
   "metadata": {},
   "source": [
    "Uploading your frontend to Vercel after uploading it to GitHub is a fairly straightforward process. Vercel integrates seamlessly with GitHub, making the deployment process easy. Here's how to do it step by step:\n",
    "\n",
    "### Step 1: Prepare Your GitHub Repository\n",
    "\n",
    "Before you start, make sure your frontend project is up to date on GitHub. This includes all necessary files to run your application, such as `package.json`, source code files, etc.\n",
    "\n",
    "### Step 2: Create a Vercel Account (if you don't have one yet)\n",
    "\n",
    "If you don't have a Vercel account yet, go to [Vercel.com](https://vercel.com/) and sign up. You can do this using your GitHub account, which facilitates integration.\n",
    "\n",
    "### Step 3: Connect Your GitHub Repository with Vercel\n",
    "\n",
    "1. **Log in to Vercel**: Log into your Vercel account.\n",
    "\n",
    "2. **Import Your Project**:\n",
    "   - In your Vercel dashboard, look for an option to \"Import Project\" or \"New Project\".\n",
    "   - Select \"Import from GitHub\". Vercel will ask for permission to access your GitHub repositories if it's your first time doing this.\n",
    "   - Choose the GitHub repository that contains your frontend project.\n",
    "\n",
    "### Step 4: Configure Your Project in Vercel\n",
    "\n",
    "Once you have selected your repository:\n",
    "\n",
    "1. **Configure Project Options**:\n",
    "   - Vercel will automatically detect that it's a frontend project (such as a React, Vue, Next.js project, etc.) and will suggest configurations.\n",
    "   - Configure the build and deployment options as necessary. This may include build commands, output directory, and environment variables.\n",
    "\n",
    "2. **Set Up Environment Variables** (if necessary):\n",
    "   - If your application requires environment variables (like API keys or URLs), add them in the project configuration on Vercel.\n",
    "\n",
    "### Step 5: Deploy Your Project\n",
    "\n",
    "- After configuring your project, click on \"Deploy\". Vercel will start the deployment process automatically.\n",
    "- You can follow the progress of the deployment in the Vercel dashboard. Once the deployment is complete, you will receive a URL where your application will be available.\n",
    "\n",
    "### Step 6: Future Updates\n",
    "\n",
    "- For future updates, simply push your changes to the GitHub repository. If you have continuous integrations enabled in Vercel, each push to the selected branch (such as `main` or `master`) will automatically initiate a new deployment.\n",
    "\n",
    "### Step 7: Verify Your Application\n",
    "\n",
    "- Once your application is deployed, visit the URL provided by Vercel to ensure everything is working as expected.\n",
    "\n",
    "And with that, your frontend should be live on Vercel, accessible via a public URL, and automatically updating with each change you push to your GitHub repository."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5b9f1a90-b8c1-461c-b810-237727e3b023",
   "metadata": {},
   "source": [
    "## Enter the environment variables in Vercel: NEXT_PUBLIC_API_URL"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "857f8603-fb71-4c6b-927c-2e56b3708330",
   "metadata": {},
   "source": [
    "To ensure that your frontend deployed on Vercel connects with your backend hosted on Render.com, you need to update the `NEXT_PUBLIC_API_URL` environment variable to point to the URL of your backend service on Render.com. Here's how you can do it:\n",
    "\n",
    "### Find Your Backend URL on Render\n",
    "\n",
    "1. **Log in to Render**: Go to [Render.com](https://render.com/) and log into your account.\n",
    "2. **Locate Your Backend Service**: Look for the service you have set up for your FastAPI backend.\n",
    "3. **Copy the Service URL**: Render assigns a URL to each deployed service. Find this URL in the dashboard of your service on Render. Typically, it will be something like `https://your-backend.onrender.com`.\n",
    "\n",
    "### Update the Environment Variable in Vercel\n",
    "\n",
    "1. **Log in to Vercel**: Go to [Vercel.com](https://vercel.com/) and log into your account.\n",
    "2. **Navigate to Your Frontend Project**: Search for and select the frontend project where you need to update the environment variable.\n",
    "3. **Access Project Settings**: Look for a configuration or settings section of the project.\n",
    "4. **Edit the Environment Variables**:\n",
    "   - Find the `NEXT_PUBLIC_API_URL` variable and change its value to the URL of your backend service on Render, for example, `https://your-backend.onrender.com`.\n",
    "   - If the variable does not exist, add it with the name `NEXT_PUBLIC_API_URL` and the corresponding value for the URL of the Render service.\n",
    "\n",
    "### Considerations\n",
    "\n",
    "- **Recompilation**: When changing environment variables in Next.js projects, you may need to recompile your application for the changes to take effect.\n",
    "- **Public Variables in Next.js**: In Next.js, environment variables exposed to the browser must start with `NEXT_PUBLIC_`. Ensure this convention is maintained so your frontend application can access them.\n",
    "\n",
    "### Frontend Deployment\n",
    "\n",
    "- Once you have updated the environment variable in Vercel, your frontend application will automatically recompile and deploy with the new configuration.\n",
    "- Verify that your frontend is correctly connecting to the backend after deployment.\n",
    "\n",
    "By following these steps, your frontend on Vercel will be configured to communicate with your backend hosted on Render.com, allowing you to effectively handle requests between the frontend and backend."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0688aa4-5591-4c8b-902f-8fa74f339aab",
   "metadata": {},
   "source": [
    "## Update the CORS configuration in the backend\n",
    "In main.py, line 20:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49a2d940-f2d9-4594-95de-71804296dfc3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# origins = [\n",
    "#     \"http://localhost:3000\",\n",
    "#     \"https://yourvercelname.vercel.app/\",\n",
    "# ]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8050e073-2ff5-4ecc-9ee3-6321f1480f82",
   "metadata": {},
   "source": [
    "Or you can instead do (this is not recommended for a real-world project):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6f0cce6a-a616-41f9-8262-1ee6399cdb86",
   "metadata": {},
   "outputs": [],
   "source": [
    "# origins = [\"*\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0eef441c-2c5f-4f01-95db-e5bb34012785",
   "metadata": {},
   "source": [
    "So the CORS configuration (line 25) will be:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "088164a1-35c9-40fd-a3d5-5e1cacac82c8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# CORS configuration, needed for frontend development\n",
    "# app.add_middleware(\n",
    "#     CORSMiddleware,\n",
    "#     allow_origins=[\"*\"],\n",
    "#     allow_credentials=True,\n",
    "#     allow_methods=[\"*\"],\n",
    "#     allow_headers=[\"*\"],\n",
    "# )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b5fbd113-1513-440a-a13c-399dd6b1c357",
   "metadata": {},
   "source": [
    "## If the data does not load, try Purge Cache in Vercel\n",
    "* In Project Settings > Data Cache > Purge Everything"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e663d4e-ba6b-46d6-b0c0-67d0e17bc232",
   "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.11.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
