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.
Unique notification identifier
ID of the user who receives this notification
ID of the notification type
Internationalization key for the title
Internationalization key for the body text
Parameters for i18n string interpolation
Additional metadata for the notification
Navigation path when notification is clicked
Icon identifier for the notification
importance
NotificationImportance
required
Importance level of the notification
ISO 8601 timestamp when notification was read
Whether the notification is archived
Whether the notification is muted
ISO 8601 timestamp when notification was created
Key identifying the notification type
I18n key for the type label
Key identifying the notification category
NotificationListFilters
Filters for querying notifications.
Filter to only unread notifications
Filter by archived status
Filter by importance level
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[]>
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>
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>
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>
Array of notification IDs to mark as unread
Throws: Supabase errors if the update fails
archive
Archive notifications.
async archive(ids: string[]): Promise<void>
Array of notification IDs to archive
Throws: Supabase errors if the update fails
unarchive
Unarchive notifications.
async unarchive(ids: string[]): Promise<void>
Array of notification IDs to unarchive
Throws: Supabase errors if the update fails
mute
Mute notifications.
async mute(ids: string[]): Promise<void>
Array of notification IDs to mute
Throws: Supabase errors if the update fails
unmute
Unmute notifications.
async unmute(ids: string[]): Promise<void>
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>
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>
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
ID of the notification type
Whether this notification type is enabled
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.notification_type_id
ID of the notification type
I18n key for the notification title
I18n key for the notification body
Parameters for i18n string interpolation
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
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]);
}