Durante el desarrollo de este curso habrás notado que todo el código se escribió en el archivo main.py. Esto puede funcionar bien cuando tienes uno o dos modelos, pero ¿qué pasa si tu aplicación empieza a crecer tanto y se apila todo en un archivo? Bueno, para eso FastAPI tiene una clase llamada APIRouter que, básicamente crea apps dentro de la app principal para agrupar las path operations y hacer más escalable y legible tu código.
Estructura de carpetas y archivos
Para seguir el tutorial del curso, yo llamé mi aplicación TrinosAPI y definí la siguiente estructura de carpetas, siguiendo las recomendaciones en la documentación de FastAPI:
|--- trinos-api
| |--- __init__.py
| |--- main.py
| |--- models
| | |--- __init__.py
| | |--- user.py
| | |--- trino.py
| |
| |--- paths
| | |--- __init__.py
| | |--- user.py
| | |--- trino.py
| |
| |--- data
| | |--- users.json
| | |--- trinos.json
| |
| |--- .gitignore
| |--- requirements.txt
Modelos
User
En el archivo models/user.py se importan las librerías requeridas y se definen las clases de la entidad User, tal como indicó el profe Facundo en la clase respectiva. Mis diferencias son:
-
Implementé un campo username para usar como nombre del usuario al hacer login en la aplicación.
-
En lugar de nombrar la clase UserRegister la llamé UserFull, por ser la clase que contiene todos los campos solicitados en el formulario de registro. Para crearla aproveché el concepto de herencia múltiple que permite Pydantic en los modelos de esta forma:
...
classUserFull(User,UserLogin):
pass
Trino
En trino.py nuevamente se importan las librerías requeridas para crear la clase Trino tal como se definió en la clase. Para poder agregar el campo by se debe importar la clase User desde el módulo user.py. En mi caso, importé la clase UserBase porque considero que no es necesario guardar la fecha de nacimiento del usuario en cada trino:
#Python packages
from datetime import datetime
from typing import Optional
from uuid import UUID
#Pydantic packages
from pydantic import BaseModel
from pydantic import Field
#Local packages
from models.user import UserBase
#Class for Trino Model
classTrino(BaseModel):
id: UUID = Field(
...,
title="Trino Id"
)
content: str = Field(
...,
title="Trino content",
min_length=1,
max_length=256
)
created_at: datetime = Field(
default=datetime.now(),
)
updated_at: Optional[datetime] = Field(
default=datetime.now(),
)
by: UserBase = Field(
...,
title="User who created the trino"
)
Paths
User
Para las path operations de la clase User se crea el archivo paths/user.py.
Aquí la diferencia respecto a lo visto en clase es el uso de APIRouter. En lugar de cargar las path operations directamente a la app con el decorador @app, lo que se hace es crear un objeto de la clase APIPRouter y decorar las path functions con este objeto. Recuerda además importar los modelos desde el módulo respectivo:
#Python packages
from typing import List
import json
#FastAPI packages
from fastapi import APIRouter
from fastapi import status
from fastapi import Body
#Local packages
from models.user import User, UserFull
router = APIRouter()
@router.post(
path="/signup",
response_model=User,
status_code=status.HTTP_201_CREATED,
summary="Sign up a new user",
tags=["User"]
)
defsignup(
user: UserFull = Body(
...,
)
):
...
Trino
Repetimos lo mismo con paths/trino.py:
#Python packages
from typing import List
import json
#FastAPI packages
from fastapi import APIRouter
from fastapi import status
#Local packages
from models.trino import Trino
router = APIRouter()
#Path operations to home page
@router.get(
path="/",
response_model=List[Trino],
status_code=status.HTTP_200_OK,
summary="Get all trinos",
tags=["Home","Trino"]
)
defhome():
...
main
Finalmente debemos llevar las paths operations a la aplicación principal. Para ello importamos los objetos router de cada archivo de paths y los añadimos al objeto app:
#Python packages
#Pydantic packages
#FastAPI packages
from fastapi import FastAPI
#Local packages
from paths import user, trino
app = FastAPI()
#Includes the paths from paths folder
app.include_router(user.router)
app.include_router(trino.router)
Y ya está, de esta forma logramos separar los modelos y endpoints de manera que la aplicación sea más clara y escalable.
Tal vez exista una forma más profesional de realizar esta tarea, ojalá que el próximo curso de esta saga nos aclare este proceso.
Si quieres ver como estoy desarrollando esta aplicación (a la fecha de este tutorial aún me faltan las otras path functions) puedes entrar a mi repositorio de GitHub (https://github.com/JosueLC/trinos-api/)