> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/Ishaq74/concordia/llms.txt
> Use this file to discover all available pages before exploring further.

# Profile API

> User profile management endpoints

# Profile API

The Profile API allows authenticated users to view and update their profile information. Profiles are automatically created on first access.

## Authentication

All endpoints require:

* Valid user session
* Session cookie or Bearer token

```typescript theme={null}
Authorization: Bearer <session-token>
```

## Get user profile

Retrieve the authenticated user's profile. If no profile exists, one is created automatically.

**Endpoint:** `GET /api/profile`

<CodeGroup>
  ```bash cURL theme={null}
  curl -X GET https://your-domain.com/api/profile \
    -H "Authorization: Bearer <session-token>"
  ```

  ```typescript SDK theme={null}
  const response = await fetch('/api/profile', {
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${sessionToken}`
    }
  });
  const profile = await response.json();
  ```
</CodeGroup>

**Response:**

<ResponseField name="id" type="string">
  Profile UUID
</ResponseField>

<ResponseField name="userId" type="string">
  User ID (references auth user table)
</ResponseField>

<ResponseField name="fullName" type="string">
  User's full name
</ResponseField>

<ResponseField name="bio" type="text">
  User biography/description
</ResponseField>

<ResponseField name="avatarUrl" type="string">
  Profile picture URL
</ResponseField>

<ResponseField name="location" type="string">
  User location (city, region)
</ResponseField>

<ResponseField name="website" type="string">
  User website URL
</ResponseField>

<ResponseField name="preferredLanguage" type="string">
  Preferred language code: `"fr"`, `"en"`, `"ar"`, or `"es"`
</ResponseField>

<ResponseField name="createdAt" type="timestamp">
  Profile creation timestamp
</ResponseField>

<ResponseField name="updatedAt" type="timestamp">
  Last update timestamp
</ResponseField>

**Example response:**

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "auth-user-id",
  "fullName": "Marie Dubois",
  "bio": "Community organizer and urban gardener",
  "avatarUrl": "https://example.com/avatars/marie.jpg",
  "location": "Lyon, France",
  "website": "https://marie-gardens.fr",
  "preferredLanguage": "fr",
  "createdAt": "2024-01-15T10:30:00Z",
  "updatedAt": "2024-03-20T14:22:00Z"
}
```

## Update user profile

Update the authenticated user's profile. Only specified fields are updated.

**Endpoint:** `PATCH /api/profile`

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://your-domain.com/api/profile \
    -H "Authorization: Bearer <session-token>" \
    -H "Content-Type: application/json" \
    -d '{
      "fullName": "Marie Dubois",
      "bio": "Community organizer and urban gardener",
      "location": "Lyon, France",
      "preferredLanguage": "fr"
    }'
  ```

  ```typescript SDK theme={null}
  const response = await fetch('/api/profile', {
    method: 'PATCH',
    headers: {
      'Authorization': `Bearer ${sessionToken}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      fullName: 'Marie Dubois',
      bio: 'Community organizer and urban gardener',
      location: 'Lyon, France',
      preferredLanguage: 'fr'
    })
  });
  const updated = await response.json();
  ```
</CodeGroup>

**Request Body:**

All fields are optional. Only provided fields will be updated.

<ParamField body="fullName" type="string">
  User's full name
</ParamField>

<ParamField body="bio" type="text">
  Biography or description
</ParamField>

<ParamField body="avatarUrl" type="string">
  Profile picture URL
</ParamField>

<ParamField body="location" type="string">
  Location (city, region, country)
</ParamField>

<ParamField body="website" type="string">
  Personal website URL
</ParamField>

<ParamField body="preferredLanguage" type="string">
  Language preference: `"fr"`, `"en"`, `"ar"`, or `"es"`
</ParamField>

<Warning>
  **Security:** Only the authenticated user can update their own profile. The `userId` field cannot be changed and is enforced by the API.
</Warning>

**Response:**

Returns the updated profile object with the same structure as GET.

```json theme={null}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "userId": "auth-user-id",
  "fullName": "Marie Dubois",
  "bio": "Community organizer and urban gardener",
  "location": "Lyon, France",
  "preferredLanguage": "fr",
  "updatedAt": "2024-03-20T15:45:00Z"
}
```

## Auto-creation behavior

When a user first accesses their profile via `GET /api/profile`:

1. **Profile exists:** Returns existing profile
2. **No profile:** Automatically creates one with:
   * `fullName` populated from auth user's name
   * `preferredLanguage` set to `"fr"` (default)
   * Other fields set to `null`

This ensures every authenticated user has a profile without requiring explicit creation.

## Validation rules

The API validates update requests:

* **Allowed fields only:** Only the 6 editable fields can be updated
* **No userId changes:** The `userId` field is immutable
* **Type checking:** Fields must match expected types
* **SQL injection prevention:** All inputs are parameterized

Attempting to update disallowed fields will silently ignore them.

## Response codes

<ResponseField name="200" type="success">
  Profile retrieved or updated successfully
</ResponseField>

<ResponseField name="400" type="error">
  Invalid request body (malformed JSON)
</ResponseField>

<ResponseField name="401" type="error">
  Unauthorized - valid session required
</ResponseField>

<ResponseField name="500" type="error">
  Database error or internal server error
</ResponseField>

## Error responses

```json theme={null}
{
  "error": "AUTH_UNAUTHORIZED"
}
```

```json theme={null}
{
  "error": "invalid_json"
}
```

## Implementation reference

Source: `/src/pages/api/profile/index.ts`

The profile endpoint:

* Uses Better Auth session validation
* Queries the `profile` table with Drizzle ORM
* Auto-creates profiles on first GET request
* Validates updates against a whitelist of allowed fields
* Sets security headers (`X-Content-Type-Options: nosniff`)

## Database schema

The profile table structure:

```typescript theme={null}
profile {
  id: uuid (primary key)
  userId: text (foreign key → user.id, unique)
  fullName: text
  bio: text
  avatarUrl: text
  location: text
  website: text
  preferredLanguage: text (default: 'fr')
  createdAt: timestamp (default: now())
  updatedAt: timestamp (default: now())
}
```

<Info>
  The `preferredLanguage` field integrates with the i18n system. When set, the application UI will display in the user's preferred language across all routes.
</Info>

## See also

* [User Roles & Permissions](/guides/user-roles)
* [Authentication System](/guides/authentication)
* [Internationalization](/guides/internationalization)
* [Database Schema](/development/database/schema#profile)
