Skip to Content
Living documentation — last reviewed 2026-05-28
FeaturesPush NotificationsPush Notifications — Data Model

Push Notifications — Data Model

device_tokens

Schema: libs/db/src/lib/schema/device-tokens.ts.

ColumnTypeNotes
iduuid PK
user_iduuid NOT NULLFK CASCADE
expo_push_tokentext NOT NULL UNIQUEGlobally unique across all users
platformdevice_platform NOT NULLios | android
last_seen_attimestamptz NOT NULL DEFAULT nowRefreshed on every re-register
created_at / updated_at / deleted_attimestamptzSoft delete

Index: device_tokens_user_idx on user_id.

notification_prefs

Schema: libs/db/src/lib/schema/notification-prefs.ts.

ColumnTypeNotes
iduuid PK
user_iduuid NOT NULL UNIQUEOne row per user
prefsjsonb NOT NULL DEFAULT {}{ category: { channel: boolean } }. Missing keys = opt-in

Channels: push, email. Categories: workoutAssigned, newMessage, newComment, announcement, classReminder (canonical in shared).

Multi-org isolation

  • Both tables are user-scoped, not org-scoped. A user with memberships in multiple orgs has one set of devices + one set of prefs across all orgs.
  • Per-org notification routing is a possible future need; not implemented today.

PII handling

  • Device tokens are credentials — anyone with the token can push to that device. Treat as secret.
  • Plaintext in DB. No encryption at column level.
  • Soft-delete preserves history; useful for ops debugging “did this device ever exist”.

Soft / hard delete

  • device_tokens.deleted_at soft-delete. All reads filter IS NULL.
  • Hard delete only via user cascade.
  • notification_prefs cascades on user delete.