Skip to content

Notificaciones

Endpoints para gestionar notificaciones de usuario y suscripciones push.

Los siguientes modelos de Prisma pertenecen al schema notifications:

model Notification {
id String @id @default(cuid())
userProfileId String @map("user_profile_id")
notificationTemplateId String? @map("notification_template_id")
type NotificationType
title String
body String
icon String?
badge String?
image String?
priority NotificationPriority @default(NORMAL)
requiresInteraction Boolean @default(false) @map("requires_interaction")
silent Boolean @default(false)
status NotificationStatus @default(PENDING)
scheduledAt DateTime? @map("scheduled_at")
sentAt DateTime? @map("sent_at")
deliveredAt DateTime? @map("delivered_at")
readAt DateTime? @map("read_at")
data Json?
actions Json?
relatedEntityType String? @map("related_entity_type")
relatedEntityId String? @map("related_entity_id")
failureReason String? @map("failure_reason")
retryCount Int @default(0) @map("retry_count")
expiresAt DateTime? @map("expires_at")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
notificationTemplate NotificationTemplate? @relation(fields: [notificationTemplateId], references: [id], onDelete: SetNull)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@index([userProfileId, status])
@@index([scheduledAt])
@@index([type])
@@map("notifications")
@@schema("notifications")
}
model PushSubscription {
id String @id @default(cuid())
userProfileId String @map("user_profile_id")
endpoint String
p256dh String
auth String
userAgent String? @map("user_agent")
deviceName String? @map("device_name")
isActive Boolean @default(true) @map("is_active")
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@unique([userProfileId, endpoint])
@@map("push_subscriptions")
@@schema("notifications")
}
model NotificationTemplate {
id String @id @default(cuid())
type NotificationType @unique
title String
body String
icon String?
badge String?
image String?
priority NotificationPriority @default(NORMAL)
actions Json?
requiresInteraction Boolean @default(false) @map("requires_interaction")
silent Boolean @default(false)
ttl Int? @default(86400)
variables String[]
isActive Boolean @default(true) @map("is_active")
notifications Notification[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("notification_templates")
@@schema("notifications")
}
enum NotificationType {
CLASS_REMINDER
RESERVATION_CONFIRMED
RESERVATION_CANCELLED
MEMBERSHIP_EXPIRING
PAYMENT_REMINDER
ACHIEVEMENT_UNLOCKED
STREAK_MILESTONE
GOAL_REMINDER
FRIEND_ACTIVITY
CLASS_CANCELLED
TRAINER_ASSIGNED
WORKOUT_REMINDER
SYSTEM_ANNOUNCEMENT
CUSTOM
@@schema("notifications")
}
enum NotificationPriority {
LOW
NORMAL
HIGH
URGENT
@@schema("notifications")
}
enum NotificationStatus {
PENDING
SENT
DELIVERED
READ
FAILED
EXPIRED
@@schema("notifications")
}

El sistema soporta notificaciones in-app y push notifications usando Web Push. Las notificaciones pueden ser programadas, enviadas inmediatamente, o basadas en plantillas.

Obtener notificaciones del usuario.

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

Este endpoint obtiene notificaciones del usuario con filtrado:

  1. Filtros disponibles:

    • type: Tipo de notificación
    • status: PENDING, SENT, DELIVERED, READ, FAILED, EXPIRED
    • priority: Prioridad de la notificación
    • isRead: ‘true’ o ‘false’ para filtrar leídas/no leídas
    • dateFrom/dateTo: Rango de fechas
  2. Incluye:

    • userProfile (id, name)
    • notificationTemplate si se usó plantilla
  3. Ordenamiento dinámico por sortBy/sortOrder

  • read: Filtrar por status de lectura
  • type: Filtrar por tipo de notificación
  • status: Filtrar por status
  • page: Número de página
  • limit: Resultados por página
{
"data": [
{
"id": "...",
"type": "RESERVATION_CONFIRMED",
"title": "Reserva confirmada",
"body": "Tu reserva para Yoga ha sido confirmada",
"status": "DELIVERED",
"readAt": null,
"createdAt": "2023-10-27T10:00:00Z"
}
],
"pagination": {...}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications.$get({
query: {
read: 'false'
}
})

Obtener conteo de notificaciones no leídas.

  • URL: /api/v1/notifications/unread-count
  • Método: GET
  • Autenticación Requerida:

Este endpoint cuenta notificaciones no leídas:

  1. Filtra notificaciones donde:

    • userProfileId coincide
    • readAt es null
    • status no es EXPIRED
  2. Retorna solo el conteo

{
"data": {
"unreadCount": 5
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications['unread-count'].$get()

Marcar una notificación como leída.

  • URL: /api/v1/notifications/:notificationId/read
  • Método: PATCH
  • Autenticación Requerida:

Este endpoint marca una notificación como leída:

  1. Actualiza la notificación:

    • status: READ
    • readAt: Fecha actual
  2. Retorna la notificación actualizada

import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications[':notificationId'].read.$patch({
param: { notificationId: 'notification_id' }
})

Marcar todas las notificaciones del usuario como leídas.

  • URL: /api/v1/notifications/read-all
  • Método: PATCH
  • Autenticación Requerida:

Este endpoint marca todas las notificaciones como leídas:

  1. Actualiza en batch todas las notificaciones donde:

    • userProfileId coincide
    • readAt es null
  2. Establece:

    • status: READ
    • readAt: Fecha actual
  3. Retorna conteo de actualizadas

{
"data": {
"updated": 5,
"message": "5 notificaciones marcadas como leídas"
}
}

Registrar una suscripción push.

  • URL: /api/v1/notifications/subscriptions
  • Método: POST
  • Autenticación Requerida:

Este endpoint registra una suscripción push:

  1. Verifica si existe suscripción con mismo endpoint
  2. Si existe: Actualiza p256dh, auth, userAgent, deviceName y reactiva
  3. Si no existe: Crea nueva suscripción
  • endpoint: URL del servicio push
  • p256dh: Clave pública del cliente
  • auth: Secreto de autenticación
  • userAgent: Identificador del navegador
  • deviceName: Nombre del dispositivo (opcional)
{
"endpoint": "https://fcm.googleapis.com/...",
"keys": {
"p256dh": "...",
"auth": "..."
}
}
import { hcWithType } from '@vitality-gym/api/client'
const client = hcWithType('http://localhost:3000')
const res = await client.api.v1.notifications.subscriptions.$post({
json: {
endpoint: 'https://...',
keys: {
p256dh: '...',
auth: '...'
}
}
})

Eliminar una suscripción push.

  • URL: /api/v1/notifications/subscriptions/:id
  • Método: DELETE
  • Autenticación Requerida:

Este endpoint elimina una suscripción push:

  1. Elimina la suscripción por ID
  2. Retorna confirmación

Crear y enviar una notificación.

  • URL: /api/v1/notifications
  • Método: POST
  • Autenticación Requerida: Sí (Admin)

Este endpoint crea y envía una notificación:

  1. Crea la notificación en la base de datos con:

    • Todos los campos proporcionados
    • status: SENT (o PENDING si programada)
    • sentAt: Ahora (o null si programada)
  2. Si no está programada, envía inmediatamente:

    • Obtiene suscripciones push activas del usuario
    • Construye payload con título, body, icon, etc.
    • Envía a todos los dispositivos
    • Actualiza status a DELIVERED si exitoso
  3. Manejo de errores push:

    • Si error 410 (Gone): Desactiva la suscripción
{
"userProfileId": "user_id",
"type": "CUSTOM",
"title": "New Announcement",
"body": "Check out our new classes!",
"icon": "🔔",
"priority": "HIGH",
"data": { "url": "/classes" }
}

Enviar notificación a múltiples usuarios.

  • URL: /api/v1/notifications/broadcast
  • Método: POST
  • Autenticación Requerida: Sí (Admin)

Este endpoint envía notificaciones masivas:

  1. Obtiene usuarios objetivo según filtros:

    • targetRoles: Filtrar por roles específicos
    • targetUserIds: Lista específica de usuarios
  2. Valida que haya usuarios para enviar

  3. Crea notificación para cada usuario encontrado

  4. Retorna conteo de enviadas

{
"targetRoles": ["MEMBER"],
"type": "ANNOUNCEMENT",
"title": "Gym Update",
"body": "New equipment available!"
}

Obtener Plantillas de Notificación (Admin)

Section titled “Obtener Plantillas de Notificación (Admin)”

Obtener todas las plantillas de notificación.

  • URL: /api/v1/notifications/templates
  • Método: GET
  • Autenticación Requerida: Sí (Admin)

Este endpoint obtiene plantillas de notificación:

  1. Filtros disponibles:

    • type: Tipo de plantilla
    • isActive: Solo plantillas activas
  2. Ordenamiento por sortBy/sortOrder

  3. Paginación estándar

Las plantillas permiten definir notificaciones reutilizables con variables como {{userName}}, {{className}}, etc.


Enviar notificación usando una plantilla.

  • URL: /api/v1/notifications/from-template
  • Método: POST
  • Autenticación Requerida: Sí (Admin)

Este endpoint envía notificaciones desde plantilla:

  1. Obtiene plantilla por tipo
  2. Verifica que esté activa
  3. Reemplaza variables en título y body:
    • Busca {{variable}} y reemplaza con valores proporcionados
  4. Crea notificaciones para cada userProfileId
  5. Configura expiración según TTL de plantilla
{
"type": "RESERVATION_REMINDER",
"userProfileIds": ["user_1", "user_2"],
"variables": {
"className": "Yoga",
"date": "2023-10-27"
}
}

Obtener Estadísticas de Notificaciones (Admin)

Section titled “Obtener Estadísticas de Notificaciones (Admin)”

Obtener estadísticas de notificaciones.

  • URL: /api/v1/notifications/stats
  • Método: GET
  • Autenticación Requerida: Sí (Admin)

Este endpoint calcula estadísticas de notificaciones:

  1. Conteos:

    • Total de notificaciones
    • No leídas
    • Leídas
  2. Tasa de lectura: (leídas / total) × 100

  3. Agrupaciones:

    • Por status
    • Por tipo
    • Por prioridad
{
"data": {
"total": 1000,
"unread": 150,
"read": 850,
"readRate": 85.0,
"byStatus": [
{ "status": "READ", "count": 850 },
{ "status": "DELIVERED", "count": 100 }
],
"byType": [...],
"byPriority": [...]
}
}