| name | Create In-App Notification |
| description | Create or update a Baserow in-app notification for an event. Use when adding a backend `NotificationType`, wiring frontend notification rendering and routing, defining the notification target, or preventing duplicate notifications for the same event object. |
Create Baserow In-App Notifications
Use this skill when a task is to add or update an in-app notification shown in Baserow's notification center.
Do not invent a new notification architecture. This repo already has established backend and frontend patterns. Start from the closest existing notification type in the same product area: core, database, builder, automation, integration, premium, or enterprise.
First Step
Before editing, identify which shape best matches the event:
- One event sends one notification to one or more explicit users.
- One event fans out to many users and should be grouped or queued efficiently.
- One event is instance-wide and should be a broadcast notification.
- The event should update or reuse an existing notification instead of creating another one.
Then inspect the closest example before editing.
Useful starting points:
- Core notification types:
backend/src/baserow/core/notification_types.py
- Database notification types:
backend/src/baserow/contrib/database/fields/notification_types.py
- Premium notification types:
premium/backend/src/baserow_premium/row_comments/notification_types.py
- Enterprise notification types:
enterprise/backend/src/baserow_enterprise/data_scanner/notification_types.py
- Backend notification APIs:
backend/src/baserow/core/notifications/handler.py
- Backend notification base classes:
backend/src/baserow/core/notifications/registries.py
- Frontend base notification type:
web-frontend/modules/core/notificationTypes.js
What A Complete Notification Usually Needs
Most new notifications touch both sides:
- Backend
NotificationType subclass and event hook.
- Backend registration in the relevant app
ready() method.
- Frontend
NotificationType class.
- Frontend content component used in the notification list.
- Frontend registration in the relevant
plugin.js.
- Targeted backend tests, and frontend tests if the route or rendering logic is non-trivial.
Backend Pattern
Follow the existing backend shape:
- Add a typed payload container, usually a dataclass, with the minimal stable fields needed by the UI and routing.
- Implement a
NotificationType subclass.
- Add a helper like
create_notification, notify_*, or construct_notification.
- Call
NotificationHandler.create_direct_notification_for_users(...) for direct notifications.
- Use
NotificationHandler.construct_notification(...) plus UserNotificationsGrouper when batching many notifications.
- Use
NotificationHandler.create_broadcast_notification(...) only for true broadcast events.
- Register the notification type in the matching backend app.
Common backend registration points:
backend/src/baserow/core/apps.py
backend/src/baserow/contrib/database/apps.py
premium/backend/src/baserow_premium/apps.py
enterprise/backend/src/baserow_enterprise/apps.py
Frontend Pattern
If the notification must render inside the app, add the frontend type too:
- Create a frontend
NotificationType subclass with the same type string.
- Return the appropriate icon component.
- Return a content component that renders the notification text.
- Implement
getRoute(notificationData) when the notification should be clickable.
- Register the type in the relevant frontend
plugin.js.
Common frontend registration points:
web-frontend/modules/core/plugin.js
web-frontend/modules/database/plugin.js
premium/web-frontend/modules/baserow_premium/plugin.js
enterprise/web-frontend/modules/baserow_enterprise/plugin.js
Define The Target Clearly
Every notification should have a clear target: what object or page the user should land on when they click it.
Prefer storing stable identifiers in notification.data, not display-only values. Usually that means IDs plus enough names to render a readable message.
Good target payload examples:
- Row or field event:
database_id, table_id, row_id, field_id
- Comment event:
comment_id, table_id, row_id
- Workspace-scoped event:
workspace_id or object IDs resolvable within the workspace
- Admin or global event: IDs and query parameters needed for an admin route
Use these rules:
- Include the smallest set of IDs required to reconstruct the target route.
- Include names only for display or email text.
- Keep the target stable even if labels change later.
- Use
workspace=None only when the event is truly user-global or instance-global.
- If the backend email link should point to the same place, keep the backend and frontend route assumptions aligned.
There are two target implementations to consider:
- Backend
get_web_frontend_url(...)
Use this when the notification is emailed and should link into the app.
EmailNotificationTypeMixin already provides the default /notification/<workspace_id>/<notification_id> route when has_web_frontend_route = True.
Override it only when the target route cannot be expressed through that default flow.
- Frontend
getRoute(notificationData)
Return the real in-app route object based on the IDs stored in notification.data.
If the notification redirects through the generic notification route, verify the frontend route can still resolve the final location from the stored data.
Prevent Duplicate Notifications
Do not blindly create a new notification every time a signal fires. First decide whether repeated events should:
- Create a new notification every time.
- Reuse one existing unread notification for the same object.
- Suppress re-creation while a tracking record still exists.
- Mark an existing notification as read instead of creating a new one.
The repo uses several duplicate-prevention patterns already:
Pattern 1: Query for an existing active notification
Use this when the event has a natural unique object, such as an invitation, comment, sync, or scan.
Typical lookup shape:
NotificationHandler.get_notification_by(
user,
notificationrecipient__read=False,
data__contains={"some_object_id": obj.id},
)
Use this when you want at most one active notification per recipient and per object. If one already exists, do not create another. Depending on the behavior, you can:
- return early
- update the existing notification data
- mark the existing notification as read as part of a follow-up action
Choose data keys that uniquely identify the event target. If multiple objects can share the same notification type, the dedupe key must include all IDs needed to distinguish them.
Pattern 2: Persist or reuse an event-tracking row
Use this when the same source content may be removed and re-added quickly, and a raw notification query is not enough.
The rich text mention flow uses RichTextFieldMention rows to track mention existence and avoid duplicate notifications when content is briefly undone and redone. Follow that approach when the event source has lifecycle state that should outlive a single signal call.
Pattern 3: Group creation before writing recipients
Use UserNotificationsGrouper when one operation can generate many notifications across many users. This reduces fan-out overhead and avoids ad hoc per-user creation loops.
Pattern 4: Update or mark read instead of inserting
If the event resolves a prior notification, prefer updating state over inserting another notification. For example, invitation follow-up flows mark the original invitation notification as read.
Choosing A Dedupe Key
A dedupe key is usually an implicit tuple made from:
- notification
type
- recipient user
- active state, usually unread and uncleared
- one or more stable object IDs stored in
data
Examples:
- One notification per invitation per user:
type + recipient + data.invitation_id
- One notification per row comment mention per user:
type + recipient + data.comment_id
- One notification per row-field mention per user:
type + recipient + data.field_id + data.row_id
Do not dedupe on mutable names or message text.
Implementation Checklist
When adding a new notification, verify all of these:
- The
type string is unique and stable.
- The payload contains stable target IDs.
- The workspace is correct for permission-scoped listing.
- The sender is correct, or
None if there is no meaningful sender.
- Duplicate creation behavior is explicit.
- Backend registration is present.
- Frontend registration is present if the notification appears in-app.
- The route works for the intended target.
- Tests cover both creation and duplicate prevention behavior.
Testing Expectations
Add the narrowest backend tests that prove:
- The right recipients are selected.
- The notification payload contains the target IDs.
- The notification is workspace-scoped correctly.
- A duplicate event does not create an extra active notification when dedupe is required.
- The route-related data needed by the frontend is present.
Useful existing tests:
premium/backend/tests/baserow_premium_tests/row_comments/test_row_comments_notification_types.py
enterprise/backend/tests/baserow_enterprise_tests/data_scanner/test_data_scanner_notification_types.py
If you add custom frontend routing or rendering logic, add or update a focused frontend unit test near the notification type or component.
Search Patterns
Use these searches to move quickly:
Use rg -n "<pattern>" <paths> as a faster equivalent when rg is available.
grep -RInE "class .*NotificationType" backend/src premium/backend/src enterprise/backend/src
grep -RInE "notification_type_registry.register" backend/src premium/backend/src enterprise/backend/src
grep -RInE "new .*NotificationType\\(context\\)" web-frontend premium/web-frontend enterprise/web-frontend
grep -RInE "NotificationHandler\\.create_direct_notification_for_users|UserNotificationsGrouper|create_broadcast_notification" backend/src premium/backend/src enterprise/backend/src
grep -RInE "data__contains=.*_id|get_notification_by\\(" backend/src premium/backend/src enterprise/backend/src
Guardrails
- Do not create a new notification type without checking whether an existing one should be reused or updated.
- Do not store only display text if the notification needs to link back to an object.
- Do not dedupe on mutable fields like names or messages.
- Do not use broadcasts for ordinary per-user events.
- Do not skip frontend registration when the notification must render in-app.
- Do not create duplicate unread notifications for the same object unless that is explicitly the desired product behavior.