Public user profile route

Overview

Este archivo define un express.Router() con una única ruta pública para consultar perfiles de usuario. La respuesta devuelve solo un subconjunto de campos del modelo User y añade publicProfileActive como dato calculado. El handler consulta únicamente cuentas activas y rechaza identificadores malformados antes de tocar MongoDB.

Este router forma parte de la superficie pública del backend. En la composición general de services/api/src/app.js, las rutas públicas se montan junto al resto de módulos de la API. Los tests del backend ejercitan esta ruta como /api/public/users/:userId y cubren el flujo correcto, el 400 por ID inválido y el 404 para cuentas no visibles.

[!NOTE] publicProfileActive no se lee desde la base de datos. Se calcula en memoria con plan y username. Solo vale true para creator y enterprise con username presente.

How it works

flowchart TD
    Client[Client] --> Router[public routes]
    Router --> ValidateId[Validate userId]
    ValidateId -->|invalid| BadRequest400[400 ID de usuario invalido]
    ValidateId -->|valid| QueryUser[User findOne active]
    QueryUser -->|missing| NotFound404[404 Usuario no encontrado]
    QueryUser -->|found| SelectFields[Select public fields]
    SelectFields --> ComputeFlag[Compute publicProfileActive]
    ComputeFlag --> Ok200[200 success data]

El flujo empieza con req.params.userId. Luego valida el valor con mongoose.Types.ObjectId.isValid. Si el formato es incorrecto, responde 400 sin continuar. Si el ID es válido, busca un documento User activo con un conjunto reducido de campos. Después calcula publicProfileActive y devuelve la respuesta pública.

Key components

Este archivo tiene una sola pieza funcional central. El resto son dependencias directas del handler.

{
    "title": "Public user profile handler",
    "content": "[badge:public] Handler an\u00f3nimo de `GET /users/:userId`. Valida el ID, consulta `User` con `accountStatus: \"active\"`, proyecta solo campos p\u00fablicos y devuelve el perfil con `publicProfileActive` calculado.",
    "type": "function",
    "filePath": "services/api/src/modules/public/public.routes.js",
    "badges": [
        "public"
    ]
}

Data & fields

La ruta trabaja con un parámetro de ruta y con un objeto de respuesta público. El documento devuelto proviene de una consulta limitada con .select(...) y .lean().

[
    {
        "name": "userId",
        "type": "string",
        "required": true,
        "description": "Identificador del usuario en la ruta. Debe ser un MongoDB ObjectId válido."
    },
    {
        "name": "success",
        "type": "boolean",
        "required": true,
        "description": "Bandera de resultado que aparece tanto en respuestas exitosas como en errores."
    },
    {
        "name": "message",
        "type": "string",
        "description": "Mensaje de error devuelto cuando el ID es inválido o el usuario no existe."
    },
    {
        "name": "data",
        "type": "object",
        "required": true,
        "description": "Perfil público devuelto en la respuesta exitosa.",
        "children": [
            {
                "name": "name",
                "type": "string",
                "description": "Nombre visible del usuario."
            },
            {
                "name": "username",
                "type": "string",
                "description": "Nombre público o handle del usuario."
            },
            {
                "name": "bio",
                "type": "string",
                "description": "Biografía pública."
            },
            {
                "name": "photo",
                "type": "string",
                "description": "Foto de perfil."
            },
            {
                "name": "coverPhoto",
                "type": "string",
                "description": "Imagen de portada."
            },
            {
                "name": "website",
                "type": "string",
                "description": "Sitio web público."
            },
            {
                "name": "location",
                "type": "string",
                "description": "Ubicación pública."
            },
            {
                "name": "accountRole",
                "type": "string",
                "description": "Rol de la cuenta devuelto por la consulta."
            },
            {
                "name": "plan",
                "type": "string",
                "description": "Plan usado para calcular `publicProfileActive`."
            },
            {
                "name": "createdAt",
                "type": "string",
                "description": "Fecha de creación del documento devuelto."
            },
            {
                "name": "seo",
                "type": "object",
                "description": "Metadatos SEO asociados al usuario."
            },
            {
                "name": "publicProfileActive",
                "type": "boolean",
                "required": true,
                "description": "Indicador calculado que señala si el perfil público está activo."
            }
        ]
    }
]

Behaviors

  • router.get("/users/:userId") — maneja la consulta pública del usuario y devuelve 200, 400 o 404.
  • mongoose.Types.ObjectId.isValid(userId) — rechaza IDs malformados antes de ejecutar la consulta.
  • User.findOne({ _id: userId, accountStatus: "active" }) — limita la búsqueda a cuentas activas.
  • .select("name username bio photo coverPhoto website location accountRole plan createdAt seo") — expone solo los campos públicos necesarios.
  • .lean() — devuelve un objeto plano para construir la respuesta.
  • next(error) — deriva errores inesperados al middleware global de Express.

API

La ruta expuesta es un único endpoint público. El contrato está respaldado por las pruebas del backend.

GET /api/public/users/:userId

{
    "title": "Get public user profile",
    "description": "Returns a public profile for an active user and computes whether the public profile is active.",
    "method": "GET",
    "baseUrl": "https://api.example.com",
    "endpoint": "/api/public/users/:userId",
    "headers": [],
    "queryParams": [],
    "pathParams": [
        {
            "key": "userId",
            "value": "MongoDB ObjectId of the user",
            "required": true
        }
    ],
    "bodyType": "none",
    "requestBody": "",
    "formData": [],
    "rawBody": "",
    "responses": {
        "200": {
            "description": "Success",
            "body": "{\n  \"success\": true,\n  \"data\": {\n    \"name\": \"Ana\",\n    \"username\": \"ana.anunciante\",\n    \"bio\": \"...\",\n    \"photo\": \"...\",\n    \"coverPhoto\": \"...\",\n    \"website\": \"...\",\n    \"location\": \"...\",\n    \"accountRole\": \"...\",\n    \"plan\": \"creator\",\n    \"createdAt\": \"2026-01-01T12:00:00.000Z\",\n    \"seo\": {},\n    \"publicProfileActive\": true\n  }\n}"
        },
        "400": {
            "description": "Invalid user id",
            "body": "{\n  \"success\": false,\n  \"message\": \"ID de usuario inv\u00e1lido.\"\n}"
        },
        "404": {
            "description": "User not found",
            "body": "{\n  \"success\": false,\n  \"message\": \"Usuario no encontrado.\"\n}"
        }
    }
}

Workflows

El flujo es síncrono y muy corto. Primero valida el parámetro, luego consulta MongoDB y finalmente responde. No hay escrituras, colas ni tareas diferidas dentro de este archivo.

Gotchas & edge cases

[!WARNING] La ruta solo devuelve usuarios con accountStatus: "active". Las pruebas confirman que pending_deletion, anonymized_pending_purge y purged responden 404.

[!IMPORTANT] La respuesta pública excluye campos sensibles. Las pruebas verifican que email, phone, passwordHash, tokenVersion, restorationToken y retentionContact no aparecen en el payload.

[!NOTE] Si plan no es creator o enterprise, o si username es vacío, publicProfileActive queda en false. Ese cálculo ocurre después de la consulta.

services/api/src/app.js incorpora este router dentro del conjunto de rutas de la API. services/api/src/models/User.js aporta el modelo consultado por el handler. Las pruebas del backend validan el comportamiento público y las restricciones de exposición de datos.