BIO.RE
User

Update User Settings

Sparse update of preferences (locale, theme, notification flags, digest mode). Locale changes also sync to the legacy User.locale field.

PATCH /api/v1/users/settings — 🔑 Bearer

Sparse update — only fields present in the body are touched. If the user has no UserSettings row yet, one is created with the submitted values (and Prisma defaults filling the rest). When locale changes, the same value is also written back to the legacy User.locale field for backward compatibility.

UserSettings.locale is the source of truth. User.locale exists as a deprecated mirror for legacy code paths and is kept in sync by this endpoint when you patch locale. Frontend should always read locale from /users/settings, not /users/profile.

Request

Body — UpdateSettingsDto

All fields optional. Send only what you want to change.

FieldTypeValidationNotes
localestringMaxLength(10)e.g. en, tr, pt-BR
themestringMaxLength(20)e.g. dark, light, system
emailNotificationsboolean
pushNotificationsboolean
inAppNotificationsboolean
digestModestringIsIn(['DAILY','WEEKLY','NONE'])
HeaderRequiredNotes
Authorization: Bearer <accessToken>JWT from POST /auth/login

Response

200 OKApiResponseOf<UserSettingsDto>

Returns the fresh settings row after update (same shape as GET /users/settings):

{
  "success": true,
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "userId": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
    "locale": "tr",
    "theme": "dark",
    "emailNotifications": true,
    "pushNotifications": false,
    "inAppNotifications": true,
    "digestMode": "WEEKLY",
    "createdAt": "2026-04-29T08:30:00.000Z",
    "updatedAt": "2026-04-29T20:00:00.000Z"
  }
}

Errors

HTTPcode / i18nKeyReason
400(DTO validation)Invalid digestMode enum, fields exceeding length
401(guard)Missing / invalid bearer token

Side effects

  1. Lookup existing UserSettings row (may be null).
  2. Build sparse data object from defined keys only.
  3. If no fields supplied, return the existing row unchanged (no DB write).
  4. Inside one transaction:
    • If row exists: prisma.userSettings.update({ where: { userId }, data }).
    • If missing: prisma.userSettings.create({ data: { id: randomUUID(), userId, ...data } }).
    • If locale was supplied: also prisma.user.update({ where: { id: userId }, data: { locale } }) (legacy mirror).
  5. Audit log: [settings] Updated for user {userId}.
  6. Return a fresh findUnique of the updated row — guarantees the response reflects the post-write state.

Code samples

curl -X PATCH https://api.bio.re/api/v1/users/settings \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "locale": "tr",
    "pushNotifications": false,
    "digestMode": "WEEKLY"
  }'
type UpdateSettingsInput = {
  locale?: string;
  theme?: string;
  emailNotifications?: boolean;
  pushNotifications?: boolean;
  inAppNotifications?: boolean;
  digestMode?: 'DAILY' | 'WEEKLY' | 'NONE';
};

async function updateSettings(accessToken: string, input: UpdateSettingsInput): Promise<UserSettings> {
  const res = await fetch('https://api.bio.re/api/v1/users/settings', {
    method: 'PATCH',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(input),
  });
  const json = await res.json();
  if (!res.ok || !json.success) {
    throw Object.assign(new Error(json?.error?.message ?? 'Update settings failed'), {
      code: json?.error?.code,
    });
  }
  return json.data;
}
import { useMutation, useQueryClient } from '@tanstack/react-query';

export function useUpdateSettings() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (input: UpdateSettingsInput) => {
      const res = await fetch('/api/v1/users/settings', {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(input),
      });
      const json = await res.json();
      if (!res.ok || !json.success) {
        throw Object.assign(new Error(json?.error?.message ?? 'Update settings failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as UserSettings;
    },
    onSuccess: (data) => {
      // Write-through cache: settings is the canonical source, profile mirrors locale
      qc.setQueryData(['users', 'settings'], data);
      qc.invalidateQueries({ queryKey: ['users', 'profile'] });
      qc.invalidateQueries({ queryKey: ['auth', 'me'] });
    },
  });
}

Try it

PATCH
/api/v1/users/settings
AuthorizationBearer <token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

curl -X PATCH "https://loading/api/v1/users/settings" \  -H "Content-Type: application/json" \  -d '{}'
{
  "success": true,
  "data": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "userId": "2c4a230c-5085-4924-a3e1-25fb4fc5965b",
    "locale": "en",
    "theme": "dark",
    "emailNotifications": true,
    "pushNotifications": true,
    "inAppNotifications": true,
    "digestMode": "DAILY",
    "createdAt": "2019-08-24T14:15:22Z",
    "updatedAt": "2019-08-24T14:15:22Z"
  }
}
{
  "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"
  }
}
{
  "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

SourcePathLines
Controllerapps/api-core/src/modules/user/user.controller.ts147–154 (updateSettings)
DTO (request)apps/api-core/src/modules/user/dto/index.ts51–69 (UpdateSettingsDto)
DTO (response)apps/api-core/src/modules/user/dto/user-client-response.dto.ts134–164 (UserSettingsDto)
Serviceapps/api-core/src/modules/user/user.service.ts362–392 (updateSettings)
Prisma modelspackages/prisma/prisma/schema.prismaUserSettings, User.locale (legacy mirror)

On this page