Creating New Identity Notification Types
This skill guides you through the complete process of creating a new identity notification type in the 6529 SEIZE Backend.
Overview
Identity notifications alert users about activities relevant to them (mentions, votes, replies, etc.). Creating a new notification type involves eight steps:
- •Add enum value to
IdentityNotificationCause - •Define data interface and notification type
- •Add mapper case for database-to-domain conversion
- •Create notifier method in
UserNotifier - •Wire up the notifier call in the triggering code
- •Add push notification handler
- •Add API service mapping (two switch cases)
- •Update OpenAPI schema and regenerate
Required Information
Before implementing, gather these details using AskUserQuestion:
1. Notification Type Name
- •What should the notification be called?
- •Use UPPER_SNAKE_CASE for the enum value
- •Examples:
DROP_PINNED,WAVE_ARCHIVED,PROFILE_UPDATED
2. Data Fields
- •What information does this notification need to store?
- •Available entity columns (use these when possible):
- •
identity_id- WHO receives the notification (required) - •
additional_identity_id- WHO triggered it (nullable) - •
related_drop_id+related_drop_part_no- Primary drop reference (nullable) - •
related_drop_2_id+related_drop_2_part_no- Secondary drop reference (nullable) - •
wave_id- Associated wave (nullable) - •
visibility_group_id- Group visibility restriction (nullable) - •
additional_data- JSON column for custom fields
- •
3. Trigger Location
- •Where in the codebase should this notification be created?
- •Common locations:
- •Drop actions:
src/drops/create-or-update-drop.use-case.ts - •Voting:
src/drops/vote-for-drop.use-case.ts - •Reactions:
src/reactions/reactions.service.ts - •Waves:
src/api-serverless/src/waves/wave.api.service.ts - •Subscriptions:
src/api-serverless/src/identity-subscriptions/identity-subscriptions.api.service.ts
- •Drop actions:
4. Push Notification Content
- •What should the push notification title say?
- •What should the body contain?
- •What deep link should it open? (profile, waves, etc.)
Implementation Steps
Step 1: Add Enum Value
File: src/entities/IIdentityNotification.ts
Add the new cause to the enum:
export enum IdentityNotificationCause {
// ... existing values
YOUR_NEW_CAUSE = 'YOUR_NEW_CAUSE'
}
Step 2: Define Type Interfaces
File: src/notifications/user-notification.types.ts
Add three things:
2a. Data interface (what gets stored):
export interface YourNewNotificationData {
actor_id: string; // Who triggered it
recipient_id: string; // Who receives it
drop_id?: string; // Related drop if applicable
wave_id?: string; // Related wave if applicable
// Add custom fields as needed
}
2b. Full notification interface:
export interface YourNewNotification extends UserNotificationBase {
cause: IdentityNotificationCause.YOUR_NEW_CAUSE;
data: YourNewNotificationData;
}
2c. Add to union type (at the end of file):
export type UserNotification = | IdentitySubscriptionNotification // ... existing types | YourNewNotification; // Add here
Step 3: Add Mapper Case
File: src/notifications/user-notification.mapper.ts
3a. Add case to switch in entityToNotification():
case IdentityNotificationCause.YOUR_NEW_CAUSE: return this.mapYourNewNotification(entity);
3b. Add mapping method:
private mapYourNewNotification(
entity: IdentityNotificationDeserialized
): YourNewNotification {
return {
id: entity.id,
created_at: entity.created_at,
read_at: entity.read_at,
cause: IdentityNotificationCause.YOUR_NEW_CAUSE,
data: {
actor_id: entity.additional_identity_id!,
recipient_id: entity.identity_id,
drop_id: entity.related_drop_id ?? undefined,
wave_id: entity.wave_id ?? undefined
// Map additional_data fields if needed:
// custom_field: entity.additional_data.custom_field
}
};
}
Important: Use ! only when the field is guaranteed to be populated for this notification type.
Step 4: Create Notifier Method
File: src/notifications/user.notifier.ts
4a. Import your new type at the top:
import {
// ... existing imports
YourNewNotificationData
} from './user-notification.types';
4b. Add notifier method:
public async notifyOfYourNewThing(
data: YourNewNotificationData,
visibility_group_id: string | null,
connection?: ConnectionWrapper<any>
) {
// Skip self-notifications
if (data.actor_id === data.recipient_id) {
return;
}
await this.identityNotificationsDb.insertNotification(
{
identity_id: data.recipient_id,
additional_identity_id: data.actor_id,
related_drop_id: data.drop_id ?? null,
related_drop_part_no: null,
related_drop_2_id: null,
related_drop_2_part_no: null,
wave_id: data.wave_id ?? null,
cause: IdentityNotificationCause.YOUR_NEW_CAUSE,
additional_data: {
// Custom JSON fields here
},
visibility_group_id
},
connection
);
}
Step 5: Wire Up the Trigger
In the identified service/use-case, call your notifier:
await userNotifier.notifyOfYourNewThing(
{
actor_id: actorIdentityId,
recipient_id: recipientIdentityId,
drop_id: dropId,
wave_id: waveId
},
visibilityGroupId,
connection // Pass if inside transaction
);
Important considerations:
- •Call AFTER the primary action succeeds
- •Pass
connectionif inside a database transaction - •Consider if notification failure should fail the whole operation
Step 6: Add Push Notification Handler
File: src/pushNotificationsHandler/identityPushNotifications.ts
6a. Add case to generateNotificationData():
case IdentityNotificationCause.YOUR_NEW_CAUSE: return handleYourNewThing(notification, additionalEntity);
6b. Add handler function:
async function handleYourNewThing(
notification: IdentityNotificationEntity,
additionalEntity: ApiIdentity
) {
// Fetch related data if needed
const dropSerialNo = await getDropSerialNo(notification.related_drop_id);
const title = `${additionalEntity.handle} did something`;
const body = 'View details';
const imageUrl = additionalEntity.pfp;
const data = {
redirect: 'waves', // or 'profile'
wave_id: notification.wave_id,
drop_id: dropSerialNo
};
return { title, body, data, imageUrl };
}
Available helpers:
- •
getDropSerialNo(dropId)- Get drop's serial number for deep linking - •
getDropPart(notification, handle?)- Get drop content for body text - •
getDrop(notification)- Get full drop entity - •
getWaveEntityOrThrow(notificationId, waveId)- Get wave entity
Step 7: Add API Service Mapping
File: src/api-serverless/src/notifications/notifications.api.service.ts
7a. Add case to getAllRelatedIds() (~line 154):
case IdentityNotificationCause.YOUR_NEW_CAUSE: {
const data = notification.data;
profileIds.push(data.actor_id);
if (data.drop_id) {
dropIds.push(data.drop_id);
}
break;
}
7b. Add case to mapToApiNotification() (~line 225):
case IdentityNotificationCause.YOUR_NEW_CAUSE: {
const data = notification.data;
return {
id: notification.id,
created_at: notification.created_at,
read_at: notification.read_at,
cause: enums.resolveOrThrow(ApiNotificationCause, notificationCause),
related_identity: profiles[data.actor_id],
related_drops: data.drop_id ? [drops[data.drop_id]] : [],
additional_context: {
wave_id: data.wave_id
// Add custom context for frontend
}
};
}
Step 8: Update OpenAPI Schema
File: src/api-serverless/openapi.yaml
Find ApiNotificationCause enum (~line 7668) and add your value:
ApiNotificationCause:
type: string
enum:
- IDENTITY_SUBSCRIBED
- IDENTITY_MENTIONED
# ... existing values
- YOUR_NEW_CAUSE # Add here
Then regenerate types:
cd src/api-serverless && npm run restructure-openapi && npm run generate
Verification Checklist
After implementation, verify:
- • Enum value added to
IdentityNotificationCauseinsrc/entities/IIdentityNotification.ts - • Data interface created in
src/notifications/user-notification.types.ts - • Notification interface created and added to
UserNotificationunion - • Mapper case added in
src/notifications/user-notification.mapper.ts - • Notifier method created in
src/notifications/user.notifier.ts - • Notifier wired up in trigger location
- • Push handler added in
src/pushNotificationsHandler/identityPushNotifications.ts - •
getAllRelatedIds()case added in notifications API service - •
mapToApiNotification()case added in notifications API service - • OpenAPI schema updated with new enum value
- • Types regenerated (
cd src/api-serverless && npm run restructure-openapi && npm run generate) - • Tests pass (
npm test) - • Code builds (
npm run build)
Common Patterns
Pattern: Simple Identity Action
- •Use For: One user does something to another (follow, block)
- •Entity Fields:
identity_id(recipient),additional_identity_id(actor) - •Push: "{actor} did X to you"
Pattern: Drop-Related Action
- •Use For: Actions on drops (vote, react, quote, reply, mention)
- •Entity Fields: +
related_drop_id,wave_id - •Push: "{actor} did X to your drop" with drop content preview
Pattern: Multi-Drop Action
- •Use For: Actions involving two drops (quote, reply)
- •Entity Fields: +
related_drop_id(new drop),related_drop_2_id(original drop) - •Push: Deep link to new drop in wave context
Pattern: Wave Action
- •Use For: Wave-level notifications (invited, archived)
- •Entity Fields: +
wave_id - •Push: Deep link to wave
Files Reference
| File | Purpose |
|---|---|
src/entities/IIdentityNotification.ts | Entity + enum definition |
src/notifications/user-notification.types.ts | TypeScript interfaces |
src/notifications/user-notification.mapper.ts | DB → domain mapping |
src/notifications/user.notifier.ts | Notification creation methods |
src/pushNotificationsHandler/identityPushNotifications.ts | Push rendering |
src/api-serverless/src/notifications/notifications.api.service.ts | API response mapping |
src/api-serverless/openapi.yaml | API schema (line ~7668) |
Environment Requirements
For notifications to work:
- •
USER_NOTIFIER_ACTIVATED=true- Enable notification creation - •
PUSH_NOTIFICATIONS_ACTIVATED=true- Enable push sending
Important Considerations
- •Self-notifications: Always skip notifying users of their own actions
- •Transaction handling: Pass
connectionif inside a database transaction - •Wave muting: Push notifications are automatically skipped for muted waves
- •Deleted entities: Handle cases where referenced profile/drop/wave was deleted
- •TypeScript exhaustiveness: The
assertUnreachablepattern ensures you don't miss switch cases
Next Steps
- •Use
AskUserQuestionto gather the four required pieces of information:- •Notification type name
- •Data fields needed
- •Trigger location
- •Push notification content
- •Implement each step in order (1-8)
- •Run
npm run buildto verify TypeScript compilation - •Run
npm testto verify tests pass - •Test locally with
USER_NOTIFIER_ACTIVATED=true