La información del proceso de conexión de la API con el Front-end se encuentra en la siguiente página: Conexión cliente servidor.
Obtención de contenidos desde la Base de Datos Orientada a Objetos hacia el cliente (FrontEnd).
Diagrama general de la comunicación desde la BD orientada a objetos con el cliente
Este diagrama muestra cómo funciona el flujo de obtención de contenidos interactivos a través de una API desarrollada con Cornice y Pyramid, y cómo se conecta con la interfaz Swagger para documentación. A continuación se explicará paso a paso:
1. El Cliente inicia la solicitud
Todo comienza cuando el cliente (por ejemplo, una aplicación web o móvil) realiza una solicitud HTTP del tipo GET /contenidos. Esta petición busca obtener contenido interactivo desde el servidor.
2. Cornice (Servicio)
La solicitud llega a Cornice, que es un componente de la arquitectura Pyramid para definir servicios web.
Aquí se recibe y valida la solicitud. Cornice actúa como una capa intermedia entre el cliente y la lógica de negocio.
Además, Cornice también expone una ruta especial: __api/o/__services, la cual es utilizada por Swagger UI para generar automáticamente la documentación de los endpoints.
3. Swagger UI (Documentación de la API)
Gracias a esa ruta especial de Cornice, Swagger puede generar una interfaz gráfica donde se visualiza toda la documentación de la API. Desde ahí, los desarrolladores pueden probar los endpoints, ver qué parámetros se necesitan y qué respuesta se espera.
4. Vista (Handler API)
Luego, Cornice llama a una función decorada con @Service. Esa función actúa como un handler o controlador, donde se ejecuta la lógica real para obtener los datos. Esta parte se conoce como la Vista o handler de la API.
5. Acceso a la base de datos
Dentro de esa función, se hace una llamada interna en Python (por ejemplo root.get(...)) que accede directamente a la base de datos. Se recupera el contenido solicitado, ya sea un recurso interactivo, datos estáticos o dinámicos.
6. Estructura lista para JSON
Una vez recuperado el contenido, el handler construye un objeto estructurado que Cornice y Pyramid pueden convertir fácilmente en formato JSON.
7. Respuesta al Cliente
Finalmente, Cornice devuelve una respuesta HTTP 200 OK con el contenido convertido a JSON.
Este JSON contiene todos los datos del contenido solicitado, listo para ser usado en el front-end del cliente.
La API Honeycomb permite gestionar y visualizar estructuras de honeycombs (panales) representadas como grafos con nodos y aristas. Cada honeycomb es un nodo raíz con celdas (nodos hijos) distribuidas de manera circular, conectadas mediante aristas.
Python 3.x
Lenguaje base.
Pyramid
Framework web principal para crear la aplicación WSGI.
Cornice
Extensión para Pyramid que facilita la creación de APIs REST.
Cornice-Swagger
Genera automáticamente documentación OpenAPI/Swagger a partir de los recursos y docstrings de Cornice.
ZODB
Base de datos orientada a objetos usada para almacenar la jerarquía de honeycombs, celdas y sus relaciones.
pyramid_zodbconn
Integración de ZODB con Pyramid.
Jinja2
Motor de plantillas para renderizar vistas HTML (además de la API JSON).
Listar honeycombs
GET /api/v1/honeycombs
Devuelve la lista de honeycombs registrados.
Obtener un honeycomb específico
GET /api/v1/honeycombs/{name}
Devuelve la representación en grafo de un honeycomb: nodo raíz, nodos hijos y aristas.
{
"honeycombs": [
{
"id": "default",
"title": "Honeycomb principal",
"icon": "https://example.com/icon.png"
},
{
"id": "juegos",
"title": "Panal de juegos",
"icon": "https://example.com/game-icon.png"
}
]
}
Devuelve la estructura de un honeycomb específico, incluyendo:
{
"id": "e93faa5a-f290-5c0e-9c11-a8529592f7ae",
"title": "Panal de Juegos",
"nodes": [
{
"id": "e93faa5a-f290-5c0e-9c11-a8529592f7ae",
"data": {
"label": "Panal de Juegos",
"themeColor": "root",
"url": "http://localhost:6543/panal-de-juegos/",
"icon": null
},
"position": {
"x": 0,
"y": 0
},
"type": "custom",
"width": 200,
"height": 80
},
{
"id": "e883b554-6fef-432d-9cee-b3013b96ebf2",
"data": {
"label": "Juego de la Serpiente",
"themeColor": "default",
"url": "http://localhost:6543/panal-de-juegos/juego-de-serpiente/",
"icon": null
},
"position": {
"x": 300,
"y": 0
},
"type": "custom",
"width": 152,
"height": 58
},
{
"id": "cca92735-72c9-427f-95f1-d75593a99cfe",
"data": {
"label": "Juego de Unity",
"themeColor": "default",
"url": "http://localhost:6543/panal-de-juegos/juego-unity/",
"icon": null
},
"position": {
"x": -300,
"y": 3.67394039744206e-14
},
"type": "custom",
"width": 152,
"height": 58
}
],
"edges": [
{
"id": "edge-e93faa5af2905c0e9c11a8529592f7ae-e883b5546fef432d9ceeb3013b96ebf2",
"source": "e93faa5a-f290-5c0e-9c11-a8529592f7ae",
"target": "e883b554-6fef-432d-9cee-b3013b96ebf2",
"type": "custom-label"
},
{
"id": "edge-e93faa5af2905c0e9c11a8529592f7ae-cca9273572c9427f95f1d75593a99cfe",
"source": "e93faa5a-f290-5c0e-9c11-a8529592f7ae",
"target": "cca92735-72c9-427f-95f1-d75593a99cfe",
"type": "custom-label"
}
]
}
{
"id": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"title": "Convida Abejas",
"nodes": [
{
"id": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"data": {
"label": "Convida Abejas",
"themeColor": "root",
"url": "http://localhost:6543/default/",
"icon": null
},
"position": {
"x": 0,
"y": 0
},
"type": "custom",
"width": 200,
"height": 80
},
{
"id": "3af7a549-2590-4d10-a686-82659346624b",
"data": {
"label": "Introduction",
"themeColor": "default",
"url": "http://localhost:6543/default/intro/",
"icon": null
},
"position": {
"x": 300,
"y": 0
},
"type": "custom",
"width": 152,
"height": 58
},
{
"id": "df574d76-e720-4d2c-b84b-ab9cfff5c813",
"data": {
"label": "Bee Logo",
"themeColor": "default",
"url": "http://localhost:6543/default/logo/",
"icon": "🐝"
},
"position": {
"x": 1.83697019872103e-14,
"y": 300
},
"type": "custom",
"width": 152,
"height": 58
},
{
"id": "011b7013-959b-42c9-9cda-adfcef5703bc",
"data": {
"label": "Website",
"themeColor": "default",
"url": "http://localhost:6543/default/link/",
"icon": null
},
"position": {
"x": -300,
"y": 3.67394039744206e-14
},
"type": "custom",
"width": 152,
"height": 58
},
{
"id": "05181539-f5ef-42f4-b09f-2d804a73d480",
"data": {
"label": "Bee Dance",
"themeColor": "default",
"url": "http://localhost:6543/default/bee-dance/",
"icon": "🐝"
},
"position": {
"x": -5.51091059616309e-14,
"y": -300
},
"type": "custom",
"width": 152,
"height": 58
}
],
"edges": [
{
"id": "edge-7950d98f015d5e24a7d1cbd115a420e9-3af7a54925904d10a68682659346624b",
"source": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"target": "3af7a549-2590-4d10-a686-82659346624b",
"type": "custom-label"
},
{
"id": "edge-7950d98f015d5e24a7d1cbd115a420e9-df574d76e7204d2cb84bab9cfff5c813",
"source": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"target": "df574d76-e720-4d2c-b84b-ab9cfff5c813",
"type": "custom-label"
},
{
"id": "edge-7950d98f015d5e24a7d1cbd115a420e9-011b7013959b42c99cdaadfcef5703bc",
"source": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"target": "011b7013-959b-42c9-9cda-adfcef5703bc",
"type": "custom-label"
},
{
"id": "edge-7950d98f015d5e24a7d1cbd115a420e9-05181539f5ef42f4b09f2d804a73d480",
"source": "7950d98f-015d-5e24-a7d1-cbd115a420e9",
"target": "05181539-f5ef-42f4-b09f-2d804a73d480",
"type": "custom-label"
}
]
}
El endpoint @resource(path='/api/v1/node/{node_id}', ...) define un recurso REST para consultar información detallada de un nodo específico en la estructura de tu aplicación.
Permite obtener los datos de un nodo usando su identificador único (node_id).
Devuelve información como el id, título, contenido, URL, icono, nodos hijos y las conexiones (edges) asociadas a ese nodo.
Cuando se recibe una petición GET a /api/v1/node/{node_id}, busca el nodo en la estructura interna (root.nodes).
Si el nodo existe, construye un diccionario con sus datos y, si tiene hijos o edges, los incluye en la respuesta.
Si el nodo no existe, responde con un error 404.
La respuesta es un JSON con toda la información relevante del nodo, útil para visualizaciones o navegación en la interfaz.
Una vez que se borró previamente la Data.fs podemos ver en terminal los id de los contenidos almacenados en la base de datos, entonces ya podemos acceder a su información individual a partir de ese id, de este modo.
http://localhost:6543/api/v1/node/63c38b62-1450-4ee2-9879-99ed66f04515
Se podrá observar en formato JSON la información individual del nodo, de esta manera:
{
"id": "63c38b62-1450-4ee2-9879-99ed66f04515",
"label": "Bee Dance",
"contents": "",
"url": "http://localhost:6543/default/bee-dance/",
"iconUrl": "🐝",
"nodes": [],
"edges": []
}
fetch('http://localhost:6543/api/v1/honeycombs/default')
.then((res) => res.json())
.then((json) => {
if (json.nodes && json.edges) {
setNodes(json.nodes);
setEdges(json.edges);
}
})
{
"id": "uuid-del-root",
"title": "Título del Honeycomb",
"nodes": [
{ "id": "...", "data": {...}, "position": {...} },
{ "id": "...", "data": {...}, "position": {...} }
],
"edges": [
{ "id": "edge-...", "source": "...", "target": "...", "type": "custom-label" }
]
}
Endpoint @resource(path='/api/v1/drones/{userid}', ...)
request.identity (provista por la configuración de autenticación de la app). Si request.identity es false, la respuesta es 401 Unauthorized.request.matchdict['userid'] != request.identity['userid'], la respuesta es 403 Forbidden (mismatch de userid). Solo el propio usuario puede consultar su información.user = getattr(self.request, 'identity', None).url_userid = self.request.matchdict.get('userid').None -> responde 401 con JSON de error.url_userid != user.get('userid') -> responde 403 con JSON de error.
401 Unauthorized — no hay identidad en la petición.
/login para que la sesión del usuario se active

200 OK — petición válida y userid coincide.
Intentaremos poner como id del usuario el siguiente convida@unam.
Recibimos esta respuesta:

403 Forbidden — identidad presente pero userid en URL no coincide
Endpoint @resource(path='/api/v1/sipping/{nodeid}', ...)
GET: Recupera los datos para el par userid:nodeid.POST: Guarda/reemplaza los datos para el par userid:nodeid con el JSON enviado.Para hacer las pruebas necesarias con este endpoint, es necesario hacer login, haciendo uso de este endpoint: http://localhost:6543/login, esto establecerá los tokens tanto de autenticación (auth_tkt) y csrf (csrf_token).
Para poder guardarlos automáticamente en un .txt, llamado cookies.txt se puede usar el siguiente comando en terminal curl.exe -v -c cookies.txt "http://localhost:6543/login". Entonces, desde terminal se pueden hacer las pruebas.
GET
{
"interacciones_previas": [],
"estadisticas": {},
"logros": []
}
POST
request.json_body{ "error": "Invalid JSON" }
sipping_data_store[key] = payload (sobrescribe si ya existía){ "status": "ok", "saved": payload }
Podemos crear un archivo .json con la simulación de la estructura esperada y con información cargada, esto para poder mandarlo en el POST. El archivo debe verse de la siguiente manera, cabe mencionar que fue nombrado payload.json:
{
"interacciones_previas": [
{
"t": "2026-01-03",
"score": 10
}
],
"estadisticas": {
"visits": 1
},
"logros": [
"inicio"
]
}
posteriormente podemos usar el siguiente comando en terminal, para mandar la información mediante POST:
curl.exe -i -X POST "http://localhost:6543/api/v1/sipping/node-24542b5b-ece1-45fa-975e-6c54744e"
-H "Content-Type: application/json"
-H "X-CSRF-Token: 08e8cf80010e455c8a294c56ff5aa446"-b cookies.txt
--data @C:\Users\compa\OneDrive\Escritorio\CONVIDA\honeycomb\payload.json
En donde se especifíca el id del nodo al que simularemos cargarle la información, que en este caso es el nodo con el id node-24542b5b-ece1-45fa-975e-6c54744e y necesitamos especificar el csrf de la sesión que en este caso es 08e8cf80010e455c8a294c56ff5aa446 y la cookie de la sesión será incrustada directamente del archivo cookies.txt y finalmente se manda la información que se obtiene del archivo payload.json creado anteriormente.
En la terminal obtendremos una respuesta que se tiene que ver similar a esta:
HTTP/1.1 200 OK
Content-Length: 141
Content-Type: application/json
Date: Sun, 04 Jan 2026 01:41:56 GMT
Server: waitress
X-Content-Type-Options: nosniff{"status": "ok", "saved": {"interacciones_previas": [{"t": "2026-01-03", "score": 10}], "estadisticas": {"visits": 1}, "logros": ["inicio"]}}
Indicando que la información fue enviada correctamente.
Si acaso hubiera un error en la terminal se vería de esta manera
HTTP/1.1 400 Bad Request
Content-Length: 25
Content-Type: application/json
Date: Sun, 04 Jan 2026 01:52:04 GMT
Server: waitress
X-Content-Type-Options: nosniff
Ahora para verificar la información cargada a dicho nodo mediante GET, podemos hacerlo desde el navegador, visitando la siguiente ruta: http://localhost:6543/api/v1/sipping/node-24542b5b-ece1-45fa-975e-6c54744e, ahí veremos la información previamente enviada el en POST. Veremos algo así.

Endpoint @resource(path='/api/v1/userid', ...)
self.request.identity.Una vez que hicimos login mediante http://localhost:6543/login, podemos obtener el userid del usuario mediante http://localhost:6543/api/v1/userid, si el login fue exitoso y hay un usuario con una sesión activa veremos una respuesta como la siguiente
{"userid": "convida@unam.social"}
En caso de que no haya sesión activa, es decir que no se haya hecho login, obtendremos el siguiente mensaje
{"error": "Unauthorized"}