BIO.RE
User

Update User Profile

Patch displayName, bio, or avatarUrl. Only fields present in the body are updated. Returns the fresh updated profile slice.

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

Patches a small slice of the user row — displayName, bio, avatarUrl. Sparse update: only the fields you send are touched. Returns the updated row (not the full profile — call GET /users/profile for the full read).

For avatar handling, prefer the dedicated PATCH /users/avatar (it also cleans up the previous file from object storage). Setting avatarUrl here works but does not delete the old file from storage.

Request

Body — UpdateProfileDto

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

FieldTypeValidationNotes
displayNamestringMaxLength(100)Free-form display name
biostringMaxLength(500)Free-form bio
avatarUrlstringIsUrl()CDN URL — prefer PATCH /users/avatar for orphan cleanup
HeaderRequiredNotes
Authorization: Bearer <accessToken>JWT from POST /auth/login

Response

200 OKApiResponseOf<UpdatedProfileDto>

{
  "success": true,
  "data": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "displayName": "John Doe",
    "bio": "Software engineer.",
    "avatarUrl": "https://cdn.bio.re/avatars/abc.jpg",
    "username": "johndoe",
    "email": "[email protected]",
    "locale": "en"
  }
}
FieldTypeNotes
idstring (UUID)Account id
displayNamestring | nullAfter update
biostring | nullAfter update
avatarUrlstring | nullAfter update
usernamestring | nullEchoed for convenience (unchanged by this endpoint)
emailstringEchoed (unchanged)
localestring | nullEchoed (unchanged)

Errors

HTTPcode / i18nKeyReason
400(DTO validation)displayName > 100 chars, bio > 500 chars, avatarUrl not a valid URL
401(guard)Missing / invalid bearer token
404error.user.not_foundToken decoded but user row missing

Side effects

  1. Lookup User; if missing, throw not_found.
  2. Build sparse data object from defined keys only.
  3. If no fields supplied, return the existing user row unchanged (no DB write).
  4. Otherwise: prisma.user.update() with the sparse data, return the fresh selected slice.
  5. Audit log: [profile] Updated for user {userId}.

Code samples

curl -X PATCH https://api.bio.re/api/v1/users/profile \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "displayName": "John Doe",
    "bio": "Software engineer."
  }'
type UpdateProfileInput = {
  displayName?: string;
  bio?: string;
  avatarUrl?: string;
};

type UpdatedProfile = {
  id: string;
  displayName: string | null;
  bio: string | null;
  avatarUrl: string | null;
  username: string | null;
  email: string;
  locale: string | null;
};

async function updateProfile(accessToken: string, input: UpdateProfileInput): Promise<UpdatedProfile> {
  const res = await fetch('https://api.bio.re/api/v1/users/profile', {
    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 failed'), {
      code: json?.error?.code,
    });
  }
  return json.data;
}
import { useMutation, useQueryClient } from '@tanstack/react-query';

export function useUpdateProfile() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: async (input: UpdateProfileInput) => {
      const res = await fetch('/api/v1/users/profile', {
        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 failed'), {
          code: json?.error?.code,
          i18nKey: json?.error?.i18nKey,
        });
      }
      return json.data as UpdatedProfile;
    },
    onSuccess: () => {
      qc.invalidateQueries({ queryKey: ['users', 'profile'] });
      qc.invalidateQueries({ queryKey: ['auth', 'me'] });
    },
  });
}

Try it

PATCH
/api/v1/users/profile
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/profile" \  -H "Content-Type: application/json" \  -d '{}'
{
  "success": true,
  "data": {
    "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
    "displayName": "John Doe",
    "bio": "Software engineer.",
    "avatarUrl": "https://cdn.bio.re/avatars/abc.jpg",
    "username": "johndoe",
    "email": "[email protected]",
    "locale": "en"
  }
}
{
  "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.ts62–69 (updateProfile)
DTO (request)apps/api-core/src/modules/user/dto/index.ts34–43 (UpdateProfileDto)
DTO (response)apps/api-core/src/modules/user/dto/user-client-response.dto.ts88–109 (UpdatedProfileDto)
Serviceapps/api-core/src/modules/user/user.service.ts61–80 (updateProfile)
Prisma modelpackages/prisma/prisma/schema.prismaUser.displayName, User.bio, User.avatarUrl

On this page