Get User Settings
Read the user's preferences row — locale, theme, three notification toggles, digest mode. Lazily creates the default row on first access.
GET /api/v1/users/settings — 🔑 Bearer
Returns the UserSettings row for the current user. Lazy bootstrap — if the row doesn't exist yet, the server creates it with defaults on this read and returns the freshly created row, so the client never has to special-case null.
UserSettings is a separate table from User, joined by userId. Defaults are applied at the Prisma level (model defaults) — emailNotifications, pushNotifications, inAppNotifications default to true, locale / theme / digestMode default to null (UI falls back to product-level defaults).
Request
No body, no params.
| Header | Required | Notes |
|---|---|---|
Authorization: Bearer <accessToken> | ✓ | JWT from POST /auth/login |
Response
200 OK — ApiResponseOf<UserSettingsDto>
{
"success": true,
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"userId": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
"locale": "en",
"theme": "dark",
"emailNotifications": true,
"pushNotifications": true,
"inAppNotifications": true,
"digestMode": "DAILY",
"createdAt": "2026-04-29T08:30:00.000Z",
"updatedAt": "2026-04-29T19:00:00.000Z"
}
}| Field | Type | Notes |
|---|---|---|
id | string (UUID) | Settings row id |
userId | string (UUID) | Owning user id (matches the bearer token's sub) |
locale | string | null | Preferred locale code (e.g. en, tr) — also written back to legacy User.locale on update |
theme | string | null | UI theme tag (e.g. dark, light, system) |
emailNotifications | boolean | Defaults to true |
pushNotifications | boolean | Defaults to true |
inAppNotifications | boolean | Defaults to true |
digestMode | enum | null | One of: DAILY / WEEKLY / NONE |
createdAt | string (ISO 8601) | Settings row creation timestamp |
updatedAt | string (ISO 8601) | Last update timestamp |
Errors
| HTTP | code / i18nKey | Reason |
|---|---|---|
401 | (guard) | Missing / invalid bearer token |
Side effects
prisma.userSettings.findUnique({ where: { userId } }).- If missing (first access):
prisma.userSettings.create({ data: { id: randomUUID(), userId } })— Prisma applies model-level defaults; this is the only write this endpoint may perform. - Return the row. No mutations on subsequent reads.
Code samples
curl https://api.bio.re/api/v1/users/settings \
-H "Authorization: Bearer $ACCESS_TOKEN"type DigestMode = 'DAILY' | 'WEEKLY' | 'NONE';
type UserSettings = {
id: string;
userId: string;
locale: string | null;
theme: string | null;
emailNotifications: boolean;
pushNotifications: boolean;
inAppNotifications: boolean;
digestMode: DigestMode | null;
createdAt: string;
updatedAt: string;
};
async function getSettings(accessToken: string): Promise<UserSettings> {
const res = await fetch('https://api.bio.re/api/v1/users/settings', {
headers: { Authorization: `Bearer ${accessToken}` },
});
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Failed to load settings'), {
code: json?.error?.code,
});
}
return json.data;
}import { useQuery } from '@tanstack/react-query';
export const userKeys = {
settings: () => ['users', 'settings'] as const,
};
export function useSettings() {
return useQuery({
queryKey: userKeys.settings(),
queryFn: async () => {
const res = await fetch('/api/v1/users/settings');
const json = await res.json();
if (!res.ok || !json.success) {
throw Object.assign(new Error(json?.error?.message ?? 'Failed to load settings'), {
code: json?.error?.code,
i18nKey: json?.error?.i18nKey,
});
}
return json.data as UserSettings;
},
staleTime: 60_000,
});
}Try it
Authorization
bearer In: header
Response Body
application/json
application/json
curl -X GET "https://loading/api/v1/users/settings"{
"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"
}
}Source
| Source | Path | Lines |
|---|---|---|
| Controller | apps/api-core/src/modules/user/user.controller.ts | 139–145 (getSettings) |
| DTO (response) | apps/api-core/src/modules/user/dto/user-client-response.dto.ts | 134–164 (UserSettingsDto) |
| Service | apps/api-core/src/modules/user/user.service.ts | 351–360 (getSettings) |
| Prisma model | packages/prisma/prisma/schema.prisma | UserSettings |
Change Email (Request)
Start an email-change flow by re-confirming the password and sending a verification link to the NEW email. Account email only flips after the new address is verified.
Update User Settings
Sparse update of preferences (locale, theme, notification flags, digest mode). Locale changes also sync to the legacy User.locale field.