Skip to main content
The Notifications service provides a comprehensive API for managing user notifications, including filtering, bulk operations, and user preferences.

Installation

import { createNotificationsService } from '../services/notifications.service';
import { getSupabase } from '../lib/supabase';

const supabase = await getSupabase();
const notificationsService = createNotificationsService(supabase);

Types

NotificationImportance

type NotificationImportance = 'low' | 'normal' | 'high' | 'urgent';

NotificationItem

Extended notification with type and category information.
id
string
required
Unique notification identifier
user_id
string
required
ID of the user who receives this notification
notification_type_id
string
required
ID of the notification type
title_i18n_key
string
required
Internationalization key for the title
body_i18n_key
string
required
Internationalization key for the body text
i18n_params
Json
Parameters for i18n string interpolation
metadata
Json
Additional metadata for the notification
action_path
string | null
Navigation path when notification is clicked
icon_key
string | null
Icon identifier for the notification
avatar_url
string | null
URL for avatar image
importance
NotificationImportance
required
Importance level of the notification
read_at
string | null
ISO 8601 timestamp when notification was read
is_archived
boolean
required
Whether the notification is archived
is_muted
boolean
required
Whether the notification is muted
created_at
string
required
ISO 8601 timestamp when notification was created
typeKey
string
Key identifying the notification type
typeLabelI18nKey
string
I18n key for the type label
categoryKey
string
Key identifying the notification category

NotificationListFilters

Filters for querying notifications.
unread
boolean
Filter to only unread notifications
archived
boolean
Filter by archived status
muted
boolean
Filter by muted status
importance
NotificationImportance
Filter by importance level
limit
number
default:"50"
Maximum number of notifications to return

NotificationTypePreference

Represents a notification type with its category and user preference.
type
NotificationTypeRow
required
The notification type definition
category
NotificationCategoryRow | null
required
The category this type belongs to
preference
UserNotificationPreferenceRow | null
required
The user’s preference for this type, or null if not set

Class: NotificationsService

Constructor

new NotificationsService(supabase: SupabaseClient<Database>)
supabase
SupabaseClient<Database>
required
Authenticated Supabase client instance

listNotifications

Retrieve notifications with optional filters.
async listNotifications(
  filters?: NotificationListFilters
): Promise<NotificationItem[]>
filters
NotificationListFilters
Optional filters for the notification query
returns
Promise<NotificationItem[]>
Array of notifications with joined type and category data
Throws: Supabase errors if the query fails

Example

// Get unread notifications
const unread = await service.listNotifications({ unread: true });

// Get urgent notifications
const urgent = await service.listNotifications({ importance: 'urgent' });

// Get first 10 non-archived notifications
const recent = await service.listNotifications({ 
  archived: false, 
  limit: 10 
});

getUnreadCount

Get the count of unread, non-archived notifications.
async getUnreadCount(): Promise<number>
returns
Promise<number>
Count of unread notifications
Throws: Supabase errors if the query fails

Example

const count = await service.getUnreadCount();
console.log(`You have ${count} unread notifications`);

markAsRead

Mark notifications as read.
async markAsRead(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to mark as read
Throws: Supabase errors if the update fails

Example

await service.markAsRead(['notif-id-1', 'notif-id-2']);

markAsUnread

Mark notifications as unread.
async markAsUnread(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to mark as unread
Throws: Supabase errors if the update fails

archive

Archive notifications.
async archive(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to archive
Throws: Supabase errors if the update fails

unarchive

Unarchive notifications.
async unarchive(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to unarchive
Throws: Supabase errors if the update fails

mute

Mute notifications.
async mute(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to mute
Throws: Supabase errors if the update fails

unmute

Unmute notifications.
async unmute(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to unmute
Throws: Supabase errors if the update fails

setImportance

Set the importance level of notifications.
async setImportance(
  ids: string[], 
  importance: NotificationImportance
): Promise<void>
ids
string[]
required
Array of notification IDs to update
importance
NotificationImportance
required
New importance level
Throws: Supabase errors if the update fails

Example

await service.setImportance(['notif-id-1'], 'urgent');

delete

Permanently delete notifications.
async delete(ids: string[]): Promise<void>
ids
string[]
required
Array of notification IDs to delete
Throws: Supabase errors if the deletion fails
This permanently deletes notifications. Consider archiving instead for a recoverable option.

listTypePreferences

Get all notification types with user preferences.
async listTypePreferences(): Promise<NotificationTypePreference[]>
returns
Promise<NotificationTypePreference[]>
Array of notification types with their categories and user preferences
Throws: Supabase errors if the query fails

Example

const preferences = await service.listTypePreferences();

preferences.forEach(pref => {
  console.log(`${pref.type.key}: enabled=${pref.preference?.is_enabled}`);
});

upsertTypePreference

Create or update a user’s preference for a notification type.
async upsertTypePreference(input: {
  notification_type_id: string;
  is_enabled?: boolean;
  is_muted?: boolean;
  muted_until?: string | null;
}): Promise<void>
input.notification_type_id
string
required
ID of the notification type
input.is_enabled
boolean
default:"true"
Whether this notification type is enabled
input.is_muted
boolean
default:"false"
Whether this notification type is muted
input.muted_until
string | null
default:"null"
ISO 8601 timestamp until which the type should be muted
Throws:
  • Supabase errors if the operation fails
  • Error if user is not authenticated

Example

// Disable transaction notifications
await service.upsertTypePreference({
  notification_type_id: 'transaction-alerts',
  is_enabled: false
});

// Mute temporarily
await service.upsertTypePreference({
  notification_type_id: 'marketing',
  is_muted: true,
  muted_until: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString()
});

createNotification

Create a new notification for a user.
async createNotification(input: {
  user_id: string;
  notification_type_id: string;
  title_i18n_key: string;
  body_i18n_key: string;
  i18n_params?: Json;
  metadata?: Json;
  action_path?: string | null;
  icon_key?: string | null;
  avatar_url?: string | null;
  dedupe_key?: string | null;
  importance?: NotificationImportance;
}): Promise<void>
input.user_id
string
required
ID of the user to notify
input.notification_type_id
string
required
ID of the notification type
input.title_i18n_key
string
required
I18n key for the notification title
input.body_i18n_key
string
required
I18n key for the notification body
input.i18n_params
Json
default:"{}"
Parameters for i18n string interpolation
input.metadata
Json
default:"{}"
Additional metadata
input.action_path
string | null
default:"null"
Path to navigate when clicked
input.icon_key
string | null
default:"null"
Icon identifier
input.avatar_url
string | null
default:"null"
Avatar image URL
input.dedupe_key
string | null
default:"null"
Key to prevent duplicate notifications
input.importance
NotificationImportance
default:"'normal'"
Importance level
Throws: Supabase errors if the insertion fails

Example

await service.createNotification({
  user_id: userId,
  notification_type_id: 'transaction-detected',
  title_i18n_key: 'notification.transaction.title',
  body_i18n_key: 'notification.transaction.body',
  i18n_params: { amount: '$50.00', merchant: 'Starbucks' },
  action_path: '/transactions/123',
  importance: 'normal'
});

Factory Function

createNotificationsService

Factory function to create a NotificationsService instance.
function createNotificationsService(
  supabase: SupabaseClient<Database>
): NotificationsService
supabase
SupabaseClient<Database>
required
Authenticated Supabase client instance
returns
NotificationsService
New NotificationsService instance

Error Handling

All async methods throw Supabase errors on failure. Always wrap calls in try-catch blocks:
try {
  await service.markAsRead(notificationIds);
} catch (error) {
  console.error('Failed to mark notifications as read:', error);
  // Handle error appropriately
}

Dependencies

  • Supabase Client: Required for all database operations
  • Database Tables:
    • notifications: Stores notification data
    • notification_types: Defines notification types
    • notification_categories: Groups notification types
    • user_notification_preferences: Stores user preferences

Usage with React Query

Typical usage pattern with React Query hooks:
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { createNotificationsService } from '../services/notifications.service';
import { getSupabase } from '../lib/supabase';

async function getService() {
  const supabase = await getSupabase();
  return createNotificationsService(supabase);
}

export function useNotifications(filters = {}) {
  return useQuery({
    queryKey: ['notifications', 'list', filters],
    queryFn: async () => {
      const service = await getService();
      return service.listNotifications(filters);
    },
    staleTime: 30 * 1000,
  });
}

export function useMarkAsRead() {
  const queryClient = useQueryClient();
  
  return useMutation({
    mutationFn: async (ids: string[]) => {
      const service = await getService();
      await service.markAsRead(ids);
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['notifications'] });
    },
  });
}

Real-time Updates

The service can be combined with Supabase real-time subscriptions:
import { useEffect } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { getSupabase } from '../lib/supabase';

export function useNotificationsRealtime(userId: string) {
  const queryClient = useQueryClient();

  useEffect(() => {
    const setup = async () => {
      const supabase = await getSupabase();
      
      const channel = supabase
        .channel('notifications-changes')
        .on(
          'postgres_changes',
          {
            event: '*',
            schema: 'public',
            table: 'notifications',
            filter: `user_id=eq.${userId}`,
          },
          () => {
            queryClient.invalidateQueries({ 
              queryKey: ['notifications'] 
            });
          }
        )
        .subscribe();

      return () => channel.unsubscribe();
    };

    setup();
  }, [queryClient, userId]);
}

Build docs developers (and LLMs) love