Spaces:
Sleeping
title: Directorio Online API
emoji: 📒
colorFrom: blue
colorTo: indigo
sdk: docker
app_port: 7860
pinned: false
Directorio Online — Backend (FastAPI + MongoDB)
API REST genérica y fácil de extender para un directorio de negocios
organizado por categorías, con items (menús, servicios o
productos), reseñas e imágenes. Construida con FastAPI y
MongoDB (driver pymongo con su API asíncrona), autenticación
JWT (OAuth2) y validación con Pydantic v2.
Modelo de datos y relaciones
Categoria
│ (puede anidarse a sí misma vía parent_id)
└── Negocio (N)
Negocio
├── Item (N) (tipo: menu | servicio | producto)
├── Reseña (N)
└── Imagen (N) (embebida)
Item
└── Imagen (N) (embebida)
Los identificadores son el ObjectId de MongoDB, expuesto siempre como
cadena en el campo id de las respuestas.
Estructura del proyecto
app/
├── core/ Configuración FastAPI: settings, seguridad (JWT),
│ middlewares (CORS), lifespan y manejadores de excepciones.
├── database/ Conexión y cliente de MongoDB, índices y utilidades.
├── exceptions/ Excepciones propias de la aplicación.
├── models/ Modelos de dominio (lo que se guarda en la BD).
├── schemas/ Esquemas de entrada/salida con validaciones
│ (contrato entre rutas y servicios).
├── services/ Lógica de negocio (CRUD genérico + reglas por entidad).
├── rutas/ Endpoints de comunicación con los clientes.
└── main.py Punto de entrada (crea la app).
Cómo extender
Añadir una nueva entidad es directo gracias al BaseService
(app/services/base.py), que implementa el CRUD genérico:
- Define el modelo en
models/y los esquemas enschemas/. - Crea un servicio que herede de
BaseService(fijacollection_name). - Añade el router en
rutas/e inclúyelo enrutas/__init__.py. - (Opcional) Declara índices en
database/indexes.py.
Requisitos
- Python 3.11+
- MongoDB 4.4+ en ejecución (local o remoto)
Instalación
python -m venv .venv
source .venv/bin/activate # En Windows: .venv\Scripts\activate
pip install -r requirements.txt
Configuración
Copia .env.example a .env y ajusta los valores:
cp .env.example .env
Genera una clave secreta segura para SECRET_KEY:
python -c "import secrets; print(secrets.token_urlsafe(48))"
| Variable | Descripción | Por defecto |
|---|---|---|
APP_NAME |
Nombre de la aplicación | Directorio Online API |
APP_VERSION |
Versión | 1.0.0 |
APP_DESCRIPTION |
Descripción de la API | (ver .env.example) |
DEBUG |
Modo depuración | false |
API_PREFIX |
Prefijo de las rutas | /api/v1 |
PORT |
Puerto de escucha del contenedor | 7860 |
MONGODB_URI |
URI de conexión a MongoDB | mongodb://localhost:27017 |
MONGODB_DB_NAME |
Nombre de la base de datos | directorio_online |
SECRET_KEY |
Clave para firmar los JWT (obligatoria) | — |
ALGORITHM |
Algoritmo de firma del JWT | HS256 |
ACCESS_TOKEN_EXPIRE_MINUTES |
Validez del token de acceso (minutos) | 60 |
CORS_ORIGINS |
Orígenes permitidos (lista por comas o *) |
* |
Ejecución
uvicorn app.main:app --reload
- Documentación interactiva (Swagger):
http://localhost:8000/docs - Documentación alternativa (ReDoc):
http://localhost:8000/redoc - Comprobación de salud:
http://localhost:8000/health
Docker
La aplicación se empaqueta con el Dockerfile incluido. El contenedor
escucha en el puerto indicado por la variable PORT (por defecto 7860).
# Construir la imagen
docker build -t directorio-online-backend .
# Ejecutar (las variables se pasan con -e o con --env-file)
docker run --rm -p 7860:7860 \
-e SECRET_KEY="$(python -c 'import secrets; print(secrets.token_urlsafe(48))')" \
-e MONGODB_URI="mongodb+srv://usuario:password@cluster.mongodb.net" \
-e MONGODB_DB_NAME="directorio_online" \
directorio-online-backend
La API quedará disponible en http://localhost:7860 (Swagger en /docs).
Despliegue en HuggingFace Spaces (Docker)
El repositorio está listo para desplegarse como un Space de tipo Docker:
- El
README.mdincluye las metadatos del Space en su cabecera YAML (sdk: docker,app_port: 7860). - El
Dockerfileejecuta el contenedor como el usuarioUID 1000(requisito de HuggingFace) y arrancauvicornen el puerto7860.
Pasos:
- Crea un Space → SDK: Docker → Blank.
- Sube este repositorio al Space (o conéctalo a GitHub).
- En Settings → Variables and secrets, define las variables del
archivo
.env.example:- Como Secret:
SECRET_KEYyMONGODB_URI. - Como Variable: el resto (
MONGODB_DB_NAME,CORS_ORIGINS, etc.).
- Como Secret:
- El Space construye la imagen y la API queda expuesta en la URL del Space
(
https://<usuario>-<space>.hf.space), con Swagger en/docs.
MongoDB: HuggingFace no provee base de datos, así que
MONGODB_URIdebe apuntar a una instancia accesible desde internet (p. ej. MongoDB Atlas). El driver ya incluyednspython(extrapymongo[srv]) para los URIsmongodb+srv://.
Autenticación
- La lectura (GET) es pública.
- La escritura (POST/PUT/DELETE) requiere un token JWT.
# 1) Registrar un usuario
curl -X POST http://localhost:8000/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"admin","email":"admin@correo.com","password":"supersecreta"}'
# 2) Iniciar sesión (formulario OAuth2; "username" admite usuario o correo)
curl -X POST http://localhost:8000/api/v1/auth/login \
-d "username=admin&password=supersecreta"
# 3) Usar el token en las operaciones de escritura
curl -X POST http://localhost:8000/api/v1/categorias \
-H "Authorization: Bearer <ACCESS_TOKEN>" \
-H "Content-Type: application/json" \
-d '{"nombre":"Restaurantes"}'
Endpoints principales
| Método | Ruta | Auth | Descripción |
|---|---|---|---|
| POST | /api/v1/auth/register |
— | Registrar usuario |
| POST | /api/v1/auth/login |
— | Iniciar sesión (devuelve JWT) |
| GET | /api/v1/auth/me |
✓ | Usuario autenticado |
| GET | /api/v1/categorias |
— | Listar categorías |
| POST | /api/v1/categorias |
✓ | Crear categoría |
| GET | /api/v1/categorias/{id} |
— | Obtener categoría |
| PUT | /api/v1/categorias/{id} |
✓ | Actualizar categoría |
| DELETE | /api/v1/categorias/{id} |
✓ | Eliminar categoría |
| GET | /api/v1/negocios |
— | Listar negocios (filtros) |
| POST | /api/v1/negocios |
✓ | Crear negocio |
| GET | /api/v1/negocios/{id} |
— | Obtener negocio |
| PUT | /api/v1/negocios/{id} |
✓ | Actualizar negocio |
| DELETE | /api/v1/negocios/{id} |
✓ | Eliminar negocio (cascada) |
| GET | /api/v1/items |
— | Listar items (filtros) |
| POST | /api/v1/items |
✓ | Crear item |
| GET | /api/v1/items/{id} |
— | Obtener item |
| PUT | /api/v1/items/{id} |
✓ | Actualizar item |
| DELETE | /api/v1/items/{id} |
✓ | Eliminar item |
| GET | /api/v1/resenas |
— | Listar reseñas (filtros) |
| POST | /api/v1/resenas |
✓ | Crear reseña |
| GET | /api/v1/resenas/{id} |
— | Obtener reseña |
| PUT | /api/v1/resenas/{id} |
✓ | Actualizar reseña |
| DELETE | /api/v1/resenas/{id} |
✓ | Eliminar reseña |
Filtros y paginación
Todos los listados aceptan skip (≥0) y limit (1–100) y devuelven una
respuesta paginada { total, skip, limit, items }.
GET /negocios?categoria_id=...&activo=true&buscar=pepeGET /items?negocio_id=...&tipo=menu&activo=trueGET /resenas?negocio_id=...GET /categorias?parent_id=...
Reglas de negocio
- Integridad referencial: al crear un negocio se valida que la categoría exista; al crear items y reseñas se valida que el negocio exista.
- Borrado en cascada: eliminar un negocio elimina también sus items y reseñas.
- Categorías: no se puede eliminar una categoría que tenga subcategorías o negocios asociados.
- Slug único: el
slugde la categoría es único; se autogenera a partir del nombre si no se proporciona.
Calidad de código
El código sigue PEP 8 (límite de línea de 99 columnas, configurado en
setup.cfg). Para verificarlo:
pip install pycodestyle pyflakes
pyflakes app/
pycodestyle app/