Skip to content

jaumellinas/sakilaAPI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

sakilaAPI

API RESTful (API)
Acceso a Datos — 2º DAM
Jaume Llinàs Sansó

Índice

  1. Descripción general
  2. Estructura del proyecto
  3. Requisitos
  4. Variables de entorno (.env)
  5. Modelos de datos (Schemas)
    1. customer (app/schemas/customer.py)
    2. rental (app/schemas/rental.py)
    3. user (app/schemasuserpy)
  6. Endpoints principales
    1. Autenticación y usuarios
    2. Clientes (customers)
    3. Reservas (rentals)
  7. Implementación de GraphQL
  8. Despliegue en local
  9. Despliegue en entorno cloud
  10. Uso de IA y recursos

Descripción general

Note

Esta aplicación está desarrollada con FastAPI, un framework de Python.

El proyecto sakilaAPI tiene como objetivo exponer una API RESTful sobre la base de datos sakila, que permite a un usuario autenticado realizar operaciones CRUD sobre clientes (customers) y reservas (rentals).

El acceso a la API se controla mediante autenticación con tokens JWT, generados al iniciar sesión con credenciales válidas.

Important

Para poder implementar autenticación en este proyecto, deberemos crear una tabla users en la propia base de datos sakila. Yo he seguido este esquema:

CREATE TABLE user (
  user_id int NOT NULL AUTO_INCREMENT,
  username varchar(56) NOT NULL,
  email varchar(104) NOT NULL,
  hashed_password varchar(255) NOT NULL,
  disabled tinyint(1) DEFAULT '0',
  created_at timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  last_update timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (user_id),
  UNIQUE KEY username (username),
  UNIQUE KEY email (email)
);

Estructura del proyecto

sakilaAPI/
├── app/
│   ├── api/
│   │   ├── v1/
│   │   │   ├── __init__.py
│   │   │   ├── auth.py
│   │   │   ├── customers.py
│   │   │   └── rentals.py
│   │   └── deps.py
│   ├── core/
│   │   ├── config.py
│   │   └── security.py
│   ├── db/
│   │   └── database.py
│   └── schemas/
│       ├── customer.py
│       ├── rental.py
│       └── user.py
├── Dockerfile
├── README.md
├── docker-compose.yaml
├── main.py
└── requirements.txt

Requisitos

  • Python 3.12+
    • fastapi
    • uvicorn
    • mariadb o mysqlclient
    • pydantic
    • python-dotenv
    • passlib
    • python-jose

Important

Esta guía de instalación asume que el usuario ya dispone de una base de datos a la que conectarse. Por tanto, toda la parte de instalación de MariaDB y el volcado de las bases de datos al programa es omitida.

Variables de entorno (.env)

  • MARIADB_USER
  • MARIADB_PASSWORD
  • MARIADB_HOST
  • MARIADB_PORT
  • MARIADB_DATABASE
  • FASTAPI_PORT
  • SECRET_KEY
  • ACCESS_TOKEN_EXPIRE_MINUTES

Modelos de datos (Schemas)

Los modelos están definidos en app/schemas/ y controlan la validación de datos en las operaciones de entrada/salida.

  • customer: creación, actualización y respuesta de clientes.
  • rental: creación y respuesta de reservas.
  • user: registro y respuesta de usuarios autenticados.

customer (app/schemas/customer.py)

{
  "store_id": 1,
  "first_name": "Miquel Àngel",
  "last_name": "Montero",
  "email": "mamonterop@santjosepobrer.es",
  "address_id": 5,
  "active": true
}

rental (app/schemas/rental.py)

{
  "rental_date": "2024-12-02T10:00:00",
  "inventory_id": 1,
  "customer_id": 1,
  "staff_id": 1
}

user (app/schemas/user.py)

{
  "username": "jllinass",
  "email": "jllinass@alumnes.santjosepobrer.es",
  "password": "miquelangelponmeun10"
}

Endpoints principales

1. Autenticación y usuarios (user)

app/api/v1/auth.py

Método Endpoint Descripción
POST /auth/register Crea un nuevo usuario en la base de datos
POST /auth/login Autentica un usuario y devuelve un token JWT

a]. Registrar usuario

POST /auth/register
{
  "username": "jllinass",
  "email": "jllinass@alumnes.santjosepobrer.es",
  "password": "miquelangelponmeun10"
}
{
  "user_id": 1,
  "username": "jllinass",
  "email": "jllinass@alumnes.santjosepobrer.es",
  "disabled": false
}

b]. Login

POST /auth/login
{
  "username": "jllinass",
  "password": "miquelangelponmeun10"
}
{
  "access_token": "eyJhbGci...vdCI6IkpXVCJ9...",
  "token_type": "bearer"
}

Important

Este token JWT debe incluirse en la cabecera de autorización de todas las peticiones protegidas:
Authorization: Bearer <token>


2. Clientes (customer)

app/api/v1/customers.py

Método Endpoint Descripción
GET /customers/ Devuelve la lista de todos los clientes
GET /customers/{customer_id} Devuelve los datos de un cliente específico
POST /customers/ Crea un nuevo cliente
PUT /customers/{customer_id} Actualiza los datos de un cliente existente
DELETE /customers/{customer_id} Elimina un cliente

c]. Obtener todos los clientes

GET /customers/
{
  "customer_id": 1,
  "store_id": 1,
  "first_name": "MARY",
  "last_name": "SMITH",
  "email": "MARY.SMITH@sakilacustomer.org",
  "address_id": 5,
  "active": true,
  "create_date": "2006-02-14T22:04:36",
  "last_update": "2025-12-28T11:07:09"
}

d]. Obtener cliente específico

GET /customers/84
{
  "customer_id": 84,
  "store_id": 2,
  "first_name": "SARA",
  "last_name": "PERRY",
  "email": "SARA.PERRY@sakilacustomer.org",
  "address_id": 88,
  "active": true,
  "create_date": "2006-02-14T22:04:36",
  "last_update": "2006-02-15T04:57:20"
}

e]. Crear nuevo cliente

POST /customers/
{
  "store_id": 1,
  "first_name": "Beatriz",
  "last_name": "Morales",
  "email": "bmoralesg@santjosepobrer.es",
  "address_id": 8,
  "active": true
}
{
  "customer_id": 46,
  "store_id": 1,
  "first_name": "Beatriz",
  "last_name": "Morales",
  "email": "bmoralesg@santjosepobrer.es",
  "address_id": 8,
  "active": true,
  "create_date": "2024-12-02T10:00:00",
  "last_update": "2024-12-02T10:00:00"
}

f]. Actualizar cliente

PUT /customers/601
{
  "store_id": 1,
  "first_name": "Jaume",
  "last_name": "Llinàs Sansó",
  "email": "jaume@llinassanso.com",
  "address_id": 5,
  "active": false
}
{
  "customer_id": 601,
  "store_id": 1,
  "first_name": "Jaume",
  "last_name": "Llinàs Sansó",
  "email": "jaume@llinassanso.com",
  "address_id": 5,
  "active": false,
  "create_date": "2025-12-28T13:48:25",
  "last_update": "2026-01-08T16:04:48"
}

g]. Eliminar cliente concreto

DELETE /customers/601
{
  "Cliente eliminado correctamente."
}

3. Reservas (rentals)

app/api/v1/rentals.py

Método Endpoint Descripción
GET /rentals/ Lista todas las reservas
GET /rentals/{rental_id} Devuelve una reserva concreta
POST /rentals/ Crea una nueva reserva
DELETE /rentals/{rental_id} Elimina una reserva

h]. Listar todas las reservas

GET /rentals
{
  "rental_id": 16050,
  "rental_date": "2025-12-28T13:49:23",
  "inventory_id": 2134,
  "customer_id": 601,
  "return_date": "2025-12-28T13:50:10",
  "staff_id": 2,
  "last_update": "2025-12-28T13:50:10"
},
{
  "rental_id": 11739,
  "rental_date": "2006-02-14T15:16:03",
  "inventory_id": 4568,
  "customer_id": 373,
  "return_date": null,
  "staff_id": 2,
  "last_update": "2006-02-15T21:30:53"
}

i]. Devuelve una reserva concreta

GET /rentals/16050
{
  "rental_id": 16050,
  "rental_date": "2025-12-28T13:49:23",
  "inventory_id": 2134,
  "customer_id": 601,
  "return_date": "2025-12-28T13:50:10",
  "staff_id": 2,
  "last_update": "2025-12-28T13:50:10"
}

j]. - Crear nueva reserva

POST /rentals/
{
  "rental_date": "2024-12-02T10:00:00",
  "inventory_id": 10,
  "customer_id": 3,
  "staff_id": 2
}
{
  "rental_id": 78,
  "rental_date": "2024-12-02T10:00:00",
  "inventory_id": 10,
  "customer_id": 3,
  "return_date": null,
  "staff_id": 2,
  "last_update": "2024-12-02T10:00:00"
}

k]. Eliminar una reserva

DELETE /rentals/14023
{
  "Reserva eliminada correctamente."
}

Implementación de GraphQL

Tras investigar el propósito de GraphQL y analizar los requisitos de este proyecto, hemos llegado a la conclusión inicial de que implementar GraphQL en el mismo sería añadir una capa de complejidad innecesaria al proyecto, ya que de momento nuestra API no es usada en aplicaciones móviles ni recibe muchísimas peticiones simultáneas.

No obstante, si en un futuro el videoclub "sakila" se expandiera de forma considerable y nuestra API per se no diera abasto, sería interesante estudiar la implementación de esta tecnología.

Despliegue en local

Para documentar el despliegue de nuestras apps, asumiremos que el usuario no tiene ninguno de nuestros requisitos instalados excepto tener un despliegue operativo de una base de datos MariaDB o MySQL.

El proceso para llevar a cabo el despliegue en local consiste en:

  1. Instalar la última versión de Python.
  2. Instalar el conector para C de MariaDB o MySQL.
  3. Clonar nuestro proyecto de GitHub en la máquina.
  4. Abrir una terminal y navegar hasta el directorio donde se encuentra nuestra app.
  5. En dicho directorio, crear un venv y activarlo.
  6. Instalar los requisitos con pip install -r requirements.txt
  7. Rellenar el archivo .env del proyecto con las variables necesarias.
  8. Ejecutar el comando uvicorn main:app --reload.

Asumiendo que en las variables de entorno hemos escogido el puerto 8000 para FastAPI, este proceso levantará nuestra API en la URL http://127.0.0.1:8000.

Despliegue en entorno cloud

De forma extra y siendo realizada esta parte tras haber acabado la actividad en sí, he dockerizado y desplegado la aplicación en un servidor VPS propio. Mi entorno de despliegue consiste en tres contenedores:

  • sakila-api
  • mysql-server
  • nginx-proxy-manager

Para lograr conexión entre todas las partes, he creados dos redes internas: sql y inter. En la primera red están conectados sakila-api y sql para poder intercambiar información entre sí. En la segunda red están conectados sakila-api y nginx-proxy-manager, consiguiendo desplegar nuestra aplicación hacia Internet sin tener que exponer nuestra propia red interna. Además, usando Nginx agilizo la generación de certificados SSL a través del Certbot de Let's Encrypt.

Explicado esto, el proceso de despliegue es el siguiente:

  1. Clonar nuestro proyecto de GitHub en el servidor.
  2. Abrir una terminal y navegar hasta el directorio donde se encuentra nuestra app.
  3. En dicho directorio, crear un archivo .env y rellenarlo con nuestras variables de entorno.
  4. Ejecutar el comando docker compose build --no-cache.
  5. Cuando el proyecto termine de descargarse y configurarse, deployear la aplicación con el comando docker compose up.

Con estos pasos, tendríamos nuestra aplicación corriendo en el puerto 8081 de nuestro Docker. Para acceder a la aplicación en sí, accederemos al dashboard de Nginx Proxy Manager y crearemos una redirección de la URL http://sakilaweb-app-1:8081 a la URL de nuestra elección.

En mi caso, y aprovechando un dominio que reservo para este tipo de experimentos, mi contenedor con la API apunta a https://apisakila.jaume.wtf.

Uso de IA y recursos

Este proyecto ha sido mayoritariamente elaborado guiándome por posts de foros del estilo StackOverflow, así como la documentación oficial de FastAPI. No obstante, para funciones específicas como el solucionado de problemas con los tokens JWT o el afinado final de la documentación con Swagger se ha usado la inteligencia artificial Claude a modo de corrector, pasándole el script entero y comentando (no corrigiendo directamente) las partes que no le gustaban.

About

Proyecto de la asignatura de Accés a Dades enfocado en desarrollar una API RESTful con FastAPI que permite gestionar clientes y reservas de la base de datos sakila mediante autenticación con tokens JWT.

Resources

Stars

Watchers

Forks

Contributors