Skip to main content
Version: 0.13.0 (Previous)

🔔 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'}},
),
);
ConfigurationDefault / source behaviorEffect
dataStores.identitiesRequired in NotificationServiceDataStore; current routes do not read itSatisfies the service TypeScript contract.
dataStores.notificationsRequiredRead by every route and updated by the two mark-as-read routes.
configuration.authSecretsRequiredThe selected access-token adapter decrypts and verifies the token with these secrets.
configuration.authModeOmitted 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

TaskStart withContract
List one identity's notificationsfindNotificationsFeaturefindNotificationsRoute, findNotificationsSchema, and self access through isSelf
Mark one notification readupdateNotificationToReadFeatureupdateNotificationToReadFeature, updateNotificationToReadRoute, and updateNotificationToReadSchema; the caller must own the notification
Mark notifications through an anchorupdateNotificationToReadBatchFeatureupdateNotificationToReadBatchFeature, 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"

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

PagePurpose
BlocksNotification persistence, listing, and mark-as-read operations.
FeaturesSchema-to-route composers mounted by the service.
RoutesEndpoint, access, and response contracts.
SchemasRequest validation contracts.
ValidatorsAuthentication, self, and ownership guards.

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.