# How RealWorld Handles Empty String Inputs for Bio and Image Fields

> Learn how RealWorld handles empty string inputs for bio and image fields. Discover their truthy conditional checks to prevent database overwrites and maintain default frontend rendering.

- Repository: [Thinkster/realworld](https://github.com/gothinkster/realworld)
- Tags: internals
- Published: 2026-02-28

---

**RealWorld uses truthy conditional checks in the user update route to ignore empty strings and null values, preventing database overwrites while the frontend renders default avatars and blank bios for any falsy profile data.**

The gothinkster/realworld repository implements a specific normalization pattern for optional user profile fields. When updating a user via the `PUT /user` endpoint, the API deliberately handles empty string inputs for `bio` and `image` by conditionally omitting them from Prisma queries, ensuring partial updates without unintended data loss.

## Prisma Schema Design for Optional Profile Fields

In `apps/api/prisma/schema.prisma`, the User model declares both fields as optional strings with a default avatar URL for new records:

```prisma
model User {
  ...
  image String? @default("https://api.realworld.io/images/smiley-cyrus.jpeg")
  bio   String?
}

```

The TypeScript interface definitions in [`apps/api/server/models/user.model.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/models/user.model.ts) align with this schema, typing both fields as nullable (`bio: string | null` and `image: any | null`). This design allows the database to store null values while providing an automatic fallback for profile images.

## Server-Side Normalization in the Update Route

The normalization logic resides in [`apps/api/server/routes/api/user/index.put.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/user/index.put.ts). The route handler destructures the incoming request body and applies conditional object spreads that only include truthy values in the Prisma update payload:

```typescript
const { email, username, password, image, bio } = user;

await usePrisma().user.update({
  where: { id: auth.id },
  data: {
    ...(email    ? { email }    : {}),
    ...(username ? { username } : {}),
    ...(password ? { password: hashedPassword } : {}),
    ...(image    ? { image }    : {}),
    ...(bio      ? { bio }      : {}),
  },
});

```

**Key behavior:** Both empty strings (`''`) and explicit `null` evaluate as falsy in JavaScript, causing the spread operator to omit these keys entirely from the `data` object. Consequently, Prisma performs a partial update that leaves existing database values unchanged when clients submit empty strings.

## Truthy vs. Falsy Input Handling

The conditional spread creates distinct handling patterns based on input types:

- **Non-empty strings:** Truthy values trigger field inclusion in the update query, overwriting the database column with the supplied string.
- **Empty strings:** Falsy values result in key omission, preserving the existing database value whether it was previously `null` or a populated string.
- **Null values:** Similarly treated as falsy, explicit `null` inputs are ignored and do not modify the column state.

This approach prioritizes partial update safety over explicit nullification, requiring clients to intentionally omit fields they do not wish to modify rather than sending empty strings to clear them.

## E2E Testing of Empty String Scenarios

The test suite demonstrates client-side attempts to clear fields via empty strings. The helper function in [`specs/e2e/helpers/api.ts`](https://github.com/gothinkster/realworld/blob/main/specs/e2e/helpers/api.ts) forwards updates directly to the endpoint:

```typescript
export async function updateUserViaAPI(
  request: APIRequestContext,
  token: string,
  updates: { image?: string; bio?: string; username?: string; email?: string },
) {
  await request.put(`${API_BASE}/user`, {
    headers: { Authorization: `Token ${token}` },
    data: { user: updates },
  });
}

```

Test cases in [`specs/e2e/null-fields.spec.ts`](https://github.com/gothinkster/realworld/blob/main/specs/e2e/null-fields.spec.ts) utilize this helper to submit empty payloads:

```typescript
await updateUserViaAPI(request, token, { image: '' });
await updateUserViaAPI(request, token, { bio: '' });

```

Despite these requests, the server-side truthy checks prevent the database from being updated, while the test assertions verify that the UI layer correctly renders default states for the resulting unchanged (or previously null) values.

## Frontend Rendering of Empty Bio and Image Fields

The client-side implementation treats any falsy value—whether `null`, `undefined`, or empty string—as an absence of user-provided data. Profile components use nullish coalescing to display sensible defaults:

```tsx
<img
  src={user.image ?? '/default-avatar.svg'}
  alt="profile"
/>
<p>{user.bio ?? ''}</p>

```

This rendering strategy ensures consistent user experience regardless of whether the database contains `null` or the API received an empty string that was subsequently filtered out by the server logic.

## Summary

- **Schema-level:** `apps/api/prisma/schema.prisma` defines `bio` and `image` as optional `String?` fields, with `image` defaulting to a specific avatar URL for new users.
- **API-level:** [`apps/api/server/routes/api/user/index.put.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/user/index.put.ts) uses conditional spreads to filter out empty strings and null values, implementing a partial update pattern that ignores falsy inputs.
- **Database impact:** Empty string inputs do not overwrite existing column values; they are treated as "no change" signals, leaving the stored data unchanged.
- **Testing:** [`specs/e2e/helpers/api.ts`](https://github.com/gothinkster/realworld/blob/main/specs/e2e/helpers/api.ts) and [`specs/e2e/null-fields.spec.ts`](https://github.com/gothinkster/realworld/blob/main/specs/e2e/null-fields.spec.ts) demonstrate the behavior through empty string update attempts and UI rendering verification.
- **Presentation:** The frontend normalizes all falsy values to default avatars and empty paragraphs, decoupling display logic from the specific database null state.

## Frequently Asked Questions

### Why does RealWorld ignore empty strings instead of saving them as null?

The API prioritizes partial update functionality. By treating empty strings as falsy and omitting them from the Prisma query in [`index.put.ts`](https://github.com/gothinkster/realworld/blob/main/index.put.ts), the server prevents clients from accidentally clearing fields they did not intend to modify. This design allows selective updates where only provided, non-empty values trigger database changes, maintaining data integrity for omitted fields.

### How can I clear the bio or image field to null via the API?

Based on the current implementation in [`apps/api/server/routes/api/user/index.put.ts`](https://github.com/gothinkster/realworld/blob/main/apps/api/server/routes/api/user/index.put.ts), you cannot explicitly set these fields to null using empty strings or null values, as both are falsy and ignored by the conditional spread logic. The endpoint requires a truthy value to update the field, meaning once a field is populated, it can only be changed to another non-empty value rather than cleared back to null through this specific route.

### What happens when a new user registers without providing an image?

According to the Prisma schema in `apps/api/prisma/schema.prisma`, the `image` field includes the attribute `@default("https://api.realworld.io/images/smiley-cyrus.jpeg")`. When Prisma creates a new user record without an explicit image value, it automatically populates the column with this default avatar URL, ensuring every user has a valid image reference immediately upon registration.

### Does the frontend differentiate between null and empty strings for the bio field?

No, the frontend treats both identically. Using nullish coalescing operators (`??`), UI components render an empty paragraph for any falsy bio value and a default SVG for any falsy image value. This normalization ensures consistent display without requiring explicit null checks in every component, effectively treating `null`, `undefined`, and `''` as equivalent "no data" states.