Mark All Notifications Read
Bulk-flip every unread notification for the calling user to read=true. Returns the count actually updated. Scoped to caller — no cross-user leak.
POST /api/v1/notifications/read-all — 🔑 Bearer · Rate limit: 60 req / minute
Bulk-marks all of the calling user's unread notifications as read in a single updateMany. Returns the number of rows actually updated. Server-scopes the update to userId = caller AND read = false, so already-read rows are untouched and no other user's data is affected.
No filtering parameters. This endpoint marks everything unread → read. There is no "mark by event type" or "mark by date range". If you need scoped clearing, iterate over PATCH /:id/read per item from the filtered list.
Request
No body, no params. Identity comes from the bearer token.
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <accessToken> | ✓ | JWT from POST /auth/login |
Response
200 OK — ApiResponseOf<MarkAllReadResponseDto>
{
"success": true,
"data": {
"updated": 12
}
}| Field | Type | Notes |
|---|---|---|
updated | number | Count of rows actually flipped from unread → read. 0 is a normal result when there are no unread notifications. |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
401 | (guard) | Missing / invalid bearer token |
429 | (throttle) | Rate limit exceeded (60 req/min) |
Side effects
prisma.notification.updateMany({ where: { userId, read: false }, data: { read: true, readAt: new Date() } }).- Return
{ updated: result.count }. The samereadAttimestamp is applied to every row in the batch. - Already-read rows are not touched (filter excludes them, so their original
readAtis preserved).
Code samples
curl -X POST https://api.bio.re/api/v1/notifications/read-all \
-H "Authorization: Bearer $ACCESS_TOKEN"async function markAllNotificationsRead(accessToken: string): Promise<number> {
const res = await fetch('https://api.bio.re/api/v1/notifications/read-all', {
method: 'POST',
headers: { Authorization: `Bearer ${accessToken}` },
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Mark all read failed'), {
code: json?.error?.code,
});
}
return json.data.updated as number;
}import { useMutation, useQueryClient } from '@tanstack/react-query';
export function useMarkAllNotificationsRead() {
const qc = useQueryClient();
return useMutation({
mutationFn: async () => {
const res = await fetch('/api/v1/notifications/read-all', { method: 'POST' });
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Mark all read failed'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
return json.data.updated as number;
},
onSuccess: () => {
qc.invalidateQueries({ queryKey: ['notifications'] });
},
});
}Try it
Authorization
bearer In: header
Response Body
application/json
application/json
curl -X POST "https://loading/api/v1/notifications/read-all"{
"success": true,
"data": {
"updated": 0
}
}{
"success": false,
"error": {
"code": "AUTH_UNAUTHORIZED",
"message": "Invalid credentials",
"i18nKey": "auth.login.invalid_credentials",
"i18nVars": {
"field": "email"
},
"details": [
{
"message": "email must be an email"
}
],
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller (inline impl) | apps/api-core/src/modules/notification/user-notification.controller.ts | 107–118 (markAllRead — direct Prisma updateMany) |
| DTO (response) | apps/api-core/src/modules/notification/dto/notification-client-response.dto.ts | 49–52 (MarkAllReadResponseDto) |
| Prisma model | packages/prisma/prisma/schema.prisma | Notification.read, Notification.readAt |