🔔 Notification
Notification provides authenticated, receiver-scoped listing and mark-as-read operations over a notifications collection.
Start here
notificationService needs identities, notifications, and token secrets. It defaults to Bearer authentication. defService also registers a router-local JSON parser; the host-level parser below is optional for these routes but is safe and useful when the host has other JSON endpoints.
import express from 'express';
import {services} from '@nodeblocks/backend-sdk';
const app = express();
app.use(express.json());
app.use(
'/api',
services.notificationService(
{identities, notifications},
{authSecrets: {authEncSecret: 'replace-me', authSignSecret: 'replace-me'}},
),
);
| Configuration | Default / source behavior | Effect |
|---|---|---|
dataStores.identities | Required in NotificationServiceDataStore; current routes do not read it | Satisfies the service TypeScript contract. |
dataStores.notifications | Required | Read by every route and updated by the two mark-as-read routes. |
configuration.authSecrets | Required | The selected access-token adapter decrypts and verifies the token with these secrets. |
configuration.authMode | Omitted or 'bearer' reads the Authorization: Bearer <token> header | 'cookie' reads request.cookies.accessToken. |
Cookie mode requires host-installed cookie-parser before the service router. For user tokens, both adapters always compare the request fingerprint; with the default checkIp: true, an IP mismatch fails only when the user agent also differs. They read the request host but the current check does not compare it. See the authentication utilities and cookie utilities for the transport helpers.
Common tasks
| Task | Start with | Contract |
|---|---|---|
| List one identity's notifications | findNotificationsFeature | findNotificationsRoute, findNotificationsSchema, and self access through isSelf |
| Mark one notification read | updateNotificationToReadFeature | updateNotificationToReadFeature, updateNotificationToReadRoute, and updateNotificationToReadSchema; the caller must own the notification |
| Mark notifications through an anchor | updateNotificationToReadBatchFeature | updateNotificationToReadBatchFeature, updateNotificationToReadBatchRoute, and updateNotificationToReadBatchSchema |
Notification exposes no public HTTP routes: every endpoint first runs isAuthenticated(). The list and batch routes additionally require the target identityId to be the caller; the single-read route requires the caller to own the notification.
Bearer HTTP workflow
Send POST /api/notifications/notification-1/read with Authorization: Bearer <access-token> and no body. updateNotificationToReadRoute returns an empty 204 for the receiver; a non-owner fails through ownsNotification, and a missing notification reaches that validator as 403 Invalid owner ID before the route's later 404 mapping.
To list the caller's own notifications, send GET /api/notifications/identities/identity-1?page=1&limit=10 with the same header. findNotificationsRoute returns 200 with { data, metadata: { pagination } }; a different path identity is 403.
export API_BASE_URL='http://localhost:8080/api'
export ACCESS_TOKEN='replace-with-a-valid-access-token'
export IDENTITY_ID='identity-1'
export NOTIFICATION_ID='notification-1'
curl -X POST "$API_BASE_URL/notifications/$NOTIFICATION_ID/read" \
-H "authorization: Bearer $ACCESS_TOKEN"
curl "$API_BASE_URL/notifications/identities/$IDENTITY_ID?page=1&limit=10" \
-H "authorization: Bearer $ACCESS_TOKEN"
Cookie HTTP workflow
Set authMode: 'cookie', register cookie-parser before the service router, then send the same protected request with the accessToken cookie instead of the Bearer header. Missing cookies fail with 401 Unable to detect access token in the selected adapter; a successful read still returns an empty 204.
export API_BASE_URL='http://localhost:8080/api'
export ACCESS_COOKIE='accessToken=replace-with-a-valid-access-token'
export NOTIFICATION_ID='notification-1'
curl -X POST "$API_BASE_URL/notifications/$NOTIFICATION_ID/read" \
-H "cookie: $ACCESS_COOKIE"
Custom feature composition
This Bearer-mode fragment uses the same SDK composer as the service source. identities and notifications below are MongoDB collections. It mounts the three features in source order; the listing route remains self-only and every route remains authenticated.
import {partial} from 'ramda';
import {features, primitives, utils} from '@nodeblocks/backend-sdk';
const dataStores = {identities, notifications};
const configuration = {
authSecrets: {
authEncSecret: 'replace-me',
authSignSecret: 'replace-me',
},
};
const router = primitives.defService(
partial(
primitives.compose(
features.updateNotificationToReadFeature,
features.findNotificationsFeature,
features.updateNotificationToReadBatchFeature,
),
[{authenticate: utils.getBearerTokenInfo, configuration, dataStores}],
),
);
app.use('/api', router);
Reference map
| Page | Purpose |
|---|---|
| Blocks | Notification persistence, listing, and mark-as-read operations. |
| Features | Schema-to-route composers mounted by the service. |
| Routes | Endpoint, access, and response contracts. |
| Schemas | Request validation contracts. |
| Validators | Authentication, self, and ownership guards. |
Related modules
Notification service provides service-level integration, and service primitives explain the router wrapper. Common blocks provide assertHasCreatedAt and normalizeDocuments; Mongo blocks back listing. Common validators define shared authentication, self, and ownership behavior. Authentication utilities and cookie utilities define the selected access-token transports.