Skip to content

Fitness

Endpoints para seguimiento de medidas corporales y fotos.

El siguiente modelo de Prisma pertenece al schema fitness:

model BodyMeasurement {
id String @id @default(cuid())
userProfileId String @map("user_profile_id")
measurementDate DateTime @default(now()) @map("measurement_date")
weight Float
height Float
imc Float
bodyFatPercentage Float? @map("body_fat_percentage")
muscleMassPercentage Float? @map("muscle_mass_percentage")
notes String?
photoUrl String? @map("photo_url")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
updatedAt DateTime @updatedAt @map("updated_at")
@@index([userProfileId, measurementDate])
@@map("body_measurements")
@@schema("fitness")
}

Obtener medidas corporales con filtrado y paginación.

  • URL: /api/v1/fitness/measurements
  • Método: GET
  • Autenticación Requerida:

Este endpoint obtiene medidas corporales con filtrado avanzado:

  1. Filtros disponibles:

    • userProfileId: Filtrar por usuario específico
    • dateFrom/dateTo: Rango de fechas de medición
    • sortBy: Ordenar por ‘date’, ‘weight’, o ‘imc’
    • sortOrder: ‘asc’ o ‘desc’
  2. Incluye relación con userProfile (id, name)

  3. Añade clasificación IMC a cada medida:

    • Bajo peso (< 18.5)
    • Peso normal (18.5 - 24.9)
    • Sobrepeso (25 - 29.9)
    • Obesidad grado I, II, III (30+)
  4. Paginación estándar con page/limit

  • userProfileId: Filtrar por usuario
  • dateFrom: Fecha de inicio (cadena ISO)
  • dateTo: Fecha de fin (cadena ISO)
  • sortBy: Campo de ordenamiento (date, weight, imc)
  • sortOrder: Dirección de ordenamiento (asc, desc)
  • page: Número de página (por defecto: 1)
  • limit: Resultados por página (por defecto: 10)
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.fitness.measurements.$get({
query: {
dateFrom: '2023-01-01',
sortBy: 'date',
sortOrder: 'desc'
}
})

Registrar una nueva medida corporal.

  • URL: /api/v1/fitness/measurements
  • Método: POST
  • Autenticación Requerida:

Este endpoint registra una nueva medida corporal:

  1. Obtiene userProfileId del contexto de autenticación

  2. Calcula IMC automáticamente: peso / (altura_metros)²

  3. Crea la medida con:

    • Fecha (proporcionada o actual)
    • Peso, altura, IMC calculado
    • Campos opcionales: bodyFatPercentage, muscleMassPercentage, notes, photoUrl
  4. Busca medida anterior para calcular comparación:

    • weightChange: Diferencia de peso
    • weightChangePercentage: Cambio porcentual
    • imcChange: Diferencia de IMC
    • daysBetween: Días desde la última medida
  5. Retorna medida con clasificación IMC y comparación

{
"weight": 75.5,
"height": 180,
"bodyFatPercentage": 15.5,
"muscleMassPercentage": 42.0,
"measurementDate": "2023-10-27",
"notes": "Morning measurement",
"photoUrl": "https://..."
}
{
"data": {
"id": "...",
"weight": 75.5,
"height": 180,
"imc": 23.3,
"imcClassification": "Peso normal",
"comparison": {
"weightChange": -0.5,
"weightChangePercentage": -0.66,
"imcChange": -0.15,
"daysBetween": 7
}
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.fitness.measurements.$post({
json: {
weight: 75.5,
height: 180,
measurementDate: '2023-10-27'
}
})

Obtener la medida más reciente del usuario autenticado.

  • URL: /api/v1/fitness/measurements/latest
  • Método: GET
  • Autenticación Requerida:

Este endpoint obtiene la última medida del usuario:

  1. Busca la medida más reciente ordenando por measurementDate descendente
  2. Incluye userProfile (id, name)
  3. Añade clasificación IMC
  4. Lanza 404 si no hay medidas registradas
{
"data": {
"id": "...",
"weight": 75.5,
"height": 180,
"imc": 23.3,
"imcClassification": "Peso normal",
"measurementDate": "2023-10-27T00:00:00.000Z",
"bodyFatPercentage": 15.5,
"muscleMassPercentage": 42.0
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.fitness.measurements.latest.$get()

Obtener estadísticas de progreso para un período específico.

  • URL: /api/v1/fitness/measurements/stats
  • Método: GET
  • Autenticación Requerida:

Este endpoint calcula estadísticas de progreso:

  1. Determina fecha de inicio según período:

    • week: 7 días atrás
    • month: 1 mes atrás
    • quarter: 3 meses atrás
    • year: 1 año atrás
    • all: Desde el año 2000
  2. Obtiene todas las medidas del período ordenadas por fecha

  3. Calcula estadísticas de peso:

    • Actual, inicial, cambio, cambio porcentual
    • Mínimo, máximo, promedio
  4. Calcula estadísticas de IMC:

    • Actual, inicial, cambio, clasificación
    • Mínimo, máximo, promedio
  5. Composición corporal (si hay datos):

    • Cambio en grasa corporal
    • Cambio en masa muscular
  6. Historial para gráficas: Últimas 30 medidas

  • period: Período de tiempo (week, month, quarter, year, all)
{
"data": {
"totalMeasurements": 15,
"period": "month",
"dateRange": { "from": "...", "to": "..." },
"weight": {
"current": 75.5,
"initial": 78.0,
"change": -2.5,
"changePercentage": -3.2,
"min": 75.0,
"max": 78.0,
"average": 76.5
},
"imc": {
"current": 23.3,
"classification": "Peso normal"
},
"history": [...]
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.fitness.measurements.stats.$get({
query: {
period: 'month'
}
})

Subir una foto para una medida corporal.

  • URL: /api/v1/fitness/measurements/photo
  • Método: POST
  • Autenticación Requerida:

Este endpoint sube una foto de progreso:

  1. Crea ruta del archivo: {UPLOAD_DIR}/measurements/{userId}/{timestamp}.{extension}
  2. Guarda el archivo usando Bun.write con el buffer de la imagen
  3. Retorna URL pública: {BACKEND_URL}/uploads/measurements/{userId}/{filename}
  • Content-Type: multipart/form-data
  • Campo: photo (Archivo)
{
"data": {
"photoUrl": "https://api.example.com/uploads/measurements/user123/2023-10-27.jpg"
}
}

Comparar dos medidas del mismo usuario.

  • URL: /api/v1/fitness/measurements/compare
  • Método: POST
  • Autenticación Requerida:

Este endpoint compara dos medidas corporales:

  1. Obtiene ambas medidas por ID
  2. Verifica que pertenezcan al mismo usuario
  3. Identifica cuál es más antigua y cuál más reciente
  4. Calcula diferencias:
    • daysBetween: Días entre medidas
    • weightChange y porcentaje
    • imcChange y porcentaje
    • bodyFatChange (si disponible)
    • muscleMassChange (si disponible)
{
"measurementId1": "measurement_id_1",
"measurementId2": "measurement_id_2"
}
{
"data": {
"older": { "...": "..." },
"newer": { "...": "..." },
"comparison": {
"daysBetween": 30,
"weightChange": -2.5,
"weightChangePercentage": -3.2,
"imcChange": -0.8,
"bodyFatChange": -1.5,
"muscleMassChange": 0.8
}
}
}

Obtener un resumen rápido de medidas para el usuario.

  • URL: /api/v1/fitness/measurements/summary
  • Método: GET
  • Autenticación Requerida:

Este endpoint obtiene un resumen rápido:

  1. Ejecuta consultas en paralelo:

    • Última medida
    • Total de medidas
    • Primera medida
  2. Calcula datos generales:

    • totalMeasurements
    • hasData: boolean indicando si hay medidas
    • latest: Última medida con clasificación IMC
    • overall: Cambio total de peso e IMC desde la primera medida
    • daysSinceFirst: Días desde la primera medida
{
"data": {
"totalMeasurements": 15,
"hasData": true,
"latest": {
"weight": 75.5,
"imc": 23.3,
"imcClassification": "Peso normal"
},
"overall": {
"weightChange": -5.0,
"imcChange": -1.5,
"daysSinceFirst": 90
}
}
}