User profile
The business profile — job title, organisation, skills — which is a different store from user settings, with a per-property API.
Not to be confused with user settings. Settings are the user's preferences; the profile is their identity in the organisation — the information an expert-finder or a people directory displays.
| Settings | Profile | |
|---|---|---|
| API | v1 usersettings | v2 user-profile |
| Shape | Free-form, extended by you | Declared: data + customData |
| Written | Whole object, or merged | One property at a time |
| Holds | Page size, saved searches | Job title, organisation, location, skills |
The per-property API
The write calls are unusual: they address a single property, by category and name, rather than sending an object.
Prop
Type
A UserProfile is { data: UserProfileData, customData?, created?, modified? }. data declares id, mail,
fullName, and the optional jobTitle, group, organisation, location, profilePhoto and skills.
customData is the free-form part.
Reading and editing a profile
Show a profile, edit one field
import { createAtomicClient } from "@sinequa/atomic";
import type { UserProfile } from "@sinequa/atomic";
const client = createAtomicClient({
app: "<your-app>",
backendUrl: "https://<your-sinequa-server>",
});
await client.initializeConfig();
await client.auth.login();
// A profile belongs to a user id — usually the signed-in one to start with.
const principal = await client.api.principal.get();
const profile: UserProfile = await client.api.userProfile.get(principal.id);
console.log(profile.data.fullName, profile.data.jobTitle ?? "(no job title)");
/**
* Updates one property. The request URL is built from `profile.data.id`, so the
* object you pass must carry it — passing only the changed field fails.
*/
export async function setJobTitle(profile: UserProfile, jobTitle: string) {
return client.api.userProfile.patch(
{ data: { ...profile.data, jobTitle } },
"data",
"jobTitle",
);
}
export async function clearJobTitle(profile: UserProfile) {
return client.api.userProfile.deleteProperty(
{ data: profile.data },
"data",
"jobTitle",
);
}import { Injectable, inject, resource, signal } from "@angular/core";
import type { UserProfile } from "@sinequa/atomic";
import { SINEQUA } from "./sinequa";
@Injectable({ providedIn: "root" })
export class ProfileService {
private readonly client = inject(SINEQUA);
readonly userId = signal("");
readonly profile = resource({
params: () => ({ id: this.userId() }),
loader: ({ params }) =>
params.id ? this.client.api.userProfile.get(params.id) : Promise.resolve(undefined),
});
/**
* Updates one property. The URL is built from `profile.data.id`, so the object
* passed must carry it — sending only the changed field fails.
*/
async setProperty<K extends keyof UserProfile["data"]>(
key: K,
value: UserProfile["data"][K],
) {
const current = this.profile.value();
if (!current) return;
return this.client.api.userProfile.patch(
{ data: { ...current.data, [key]: value } },
"data",
String(key),
);
}
}import type { UserProfile } from "@sinequa/atomic";
import { useCallback, useEffect, useState } from "react";
import { ready, sinequa } from "./sinequa";
export function useProfile(userId: string | undefined) {
const [profile, setProfile] = useState<UserProfile>();
useEffect(() => {
if (!userId) return;
let active = true;
ready
.then(() => sinequa.api.userProfile.get(userId))
.then((next) => active && setProfile(next));
return () => {
active = false;
};
}, [userId]);
/**
* Updates one property. The URL is built from `profile.data.id`, so the object
* passed must carry it — sending only the changed field fails.
*/
const setProperty = useCallback(
async <K extends keyof UserProfile["data"]>(key: K, value: UserProfile["data"][K]) => {
if (!profile) return;
const updated = await sinequa.api.userProfile.patch(
{ data: { ...profile.data, [key]: value } },
"data",
String(key),
);
setProfile(updated);
return updated;
},
[profile],
);
return { profile, setProperty };
}