> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# findById

> Retrieve a single record by ID

`findById` is a method that retrieves a single record by ID. It loads data with the specified subset and throws a `NotFoundException` if the record doesn't exist.

<Info>
  `findById` is not defined in BaseModelClass. It's a **standard pattern** automatically generated
  by the Syncer in each Model class when you create an Entity.
</Info>

## Type Signature

```typescript theme={null}
async findById<T extends SubsetKey>(
  subset: T,
  id: number
): Promise<SubsetMapping[T]>
```

## Auto-Generated Code

Sonamu automatically generates the following code based on your Entity:

```typescript theme={null}
// src/application/user/user.model.ts (auto-generated)
class UserModelClass extends BaseModelClass {
  @api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "User" })
  async findById<T extends UserSubsetKey>(subset: T, id: number): Promise<UserSubsetMapping[T]> {
    const { rows } = await this.findMany(subset, {
      id,
      num: 1,
      page: 1,
    });
    if (!rows[0]) {
      throw new NotFoundException(`User ID ${id} not found`);
    }

    return rows[0];
  }
}
```

**How it works:**

1. Calls `findMany` to query by ID
2. Fetches only one record with `num: 1, page: 1`
3. Throws `NotFoundException` if no result
4. Returns the first record

## Parameters

### subset

Specifies the subset of data to retrieve.

**Type:** `SubsetKey` (e.g., `"A"`, `"B"`, `"C"`)

Subsets determine the shape of data as defined in your Entity configuration. Each subset can include different fields and relationships.

```typescript theme={null}
// Fetch basic info only
const user = await UserModel.findById("A", 1);

// Fetch with relationship data
const user = await UserModel.findById("B", 1);
```

### id

The ID of the record to retrieve.

**Type:** `number`

```typescript theme={null}
const user = await UserModel.findById("A", 123);
```

## Return Value

**Type:** `Promise<SubsetMapping[T]>`

Returns a record of the specified subset type. The type is automatically inferred based on the subset.

```typescript theme={null}
// user's type is automatically inferred as UserSubsetMapping["A"]
const user = await UserModel.findById("A", 1);

console.log(user.id); // number
console.log(user.email); // string
console.log(user.name); // string
```

## Exceptions

### NotFoundException

Throws `NotFoundException` if the record with the given ID doesn't exist.

```typescript theme={null}
try {
  const user = await UserModel.findById("A", 999);
} catch (error) {
  if (error instanceof NotFoundException) {
    console.error("User not found");
  }
}
```

**HTTP Status Code:** 404

## Basic Usage

### Simple Query

```typescript theme={null}
import { UserModel } from "./user/user.model";

class UserService {
  async getUser(userId: number) {
    // Fetch user by ID (subset A)
    const user = await UserModel.findById("A", userId);

    return {
      id: user.id,
      email: user.email,
      name: user.name,
    };
  }
}
```

### Usage in API

`findById` is automatically exposed as a REST API with the `@api` decorator:

```typescript theme={null}
// Auto-generated code
@api({ httpMethod: "GET", clients: ["axios", "tanstack-query"], resourceName: "User" })
async findById<T extends UserSubsetKey>(
  subset: T,
  id: number
): Promise<UserSubsetMapping[T]> {
  // ...
}
```

Generated client code:

```typescript theme={null}
// services.generated.ts (auto-generated)
import { UserService } from "@/services/UserService";

// Fetch user by ID
const user = await UserService.getUser("A", 123);
```

## Usage by Subset

### Subset A (Basic)

```typescript theme={null}
const user = await UserModel.findById("A", 1);

// Available fields
user.id; // number
user.email; // string
user.name; // string
user.created_at; // Date
```

### Subset B (With Relationships)

```typescript theme={null}
const user = await UserModel.findById("B", 1);

// Relationship data auto-loaded
user.id; // number
user.email; // string
user.name; // string
user.posts; // Post[] (1:N relationship)
user.profile; // Profile (1:1 relationship)
```

### Subset C (Nested Relationships)

```typescript theme={null}
const user = await UserModel.findById("C", 1);

// Nested relationships loaded
user.posts[0].comments; // Comment[] (nested relationship)
user.profile.images; // Image[] (nested relationship)
```

## Practical Examples

<Tabs>
  <Tab title="User Profile" icon="user">
    ```typescript theme={null}
    import { UserModel } from "./user/user.model";
    import { api } from "sonamu";

    class ProfileFrame {
      @api({ httpMethod: "GET" })
      async getProfile(userId: number) {
        // Fetch user by ID (with profile info)
        const user = await UserModel.findById("Profile", userId);

        return {
          id: user.id,
          email: user.email,
          name: user.name,
          avatar: user.profile?.avatar_url,
          bio: user.profile?.bio,
          posts_count: user.posts?.length ?? 0
        };
      }
    }
    ```
  </Tab>

  <Tab title="Order Details" icon="cart-shopping">
    ```typescript theme={null}
    import { OrderModel } from "./order/order.model";
    import { api } from "sonamu";

    class OrderFrame {
      @api({ httpMethod: "GET" })
      async getOrderDetail(orderId: number) {
        // Fetch order details (with products, shipping info)
        const order = await OrderModel.findById("Detail", orderId);

        return {
          id: order.id,
          status: order.status,
          total_amount: order.total_amount,
          items: order.items.map(item => ({
            product_name: item.product.name,
            quantity: item.quantity,
            price: item.price
          })),
          shipping: {
            address: order.shipping_address,
            status: order.shipping_status,
            tracking_number: order.tracking_number
          },
          customer: {
            name: order.user.name,
            email: order.user.email
          }
        };
      }
    }
    ```
  </Tab>

  <Tab title="Permission Check" icon="shield">
    ```typescript theme={null}
    import { UserModel } from "./user/user.model";
    import { Sonamu, api, UnauthorizedException } from "sonamu";

    class PostFrame {
      @api({ httpMethod: "POST" })
      async deletePost(postId: number) {
        const { user } = Sonamu.getContext();

        // Check post owner
        const post = await PostModel.findById("A", postId);

        if (post.user_id !== user.id) {
          throw new UnauthorizedException("You can only delete your own posts");
        }

        await PostModel.del([postId]);

        return { success: true };
      }
    }
    ```
  </Tab>

  <Tab title="Error Handling" icon="circle-exclamation">
    ```typescript theme={null}
    import { UserModel } from "./user/user.model";
    import { NotFoundException, api } from "sonamu";

    class UserFrame {
      @api({ httpMethod: "GET" })
      async getUserSafely(userId: number) {
        try {
          // Fetch user by ID
          const user = await UserModel.findById("A", userId);

          return {
            exists: true,
            user: {
              id: user.id,
              name: user.name,
              email: user.email
            }
          };
        } catch (error) {
          if (error instanceof NotFoundException) {
            return {
              exists: false,
              user: null
            };
          }
          throw error;
        }
      }

      @api({ httpMethod: "GET" })
      async getMultipleUsers(userIds: number[]) {
        const users = [];

        for (const id of userIds) {
          try {
            const user = await UserModel.findById("A", id);
            users.push(user);
          } catch (error) {
            // Skip non-existent users
            if (error instanceof NotFoundException) {
              continue;
            }
            throw error;
          }
        }

        return users;
      }
    }
    ```
  </Tab>
</Tabs>

## findById vs findOne vs findMany

### findById

* Fetches **single** record by ID
* **Throws exception** if record not found
* Simplest and fastest
* **Auto-generated**

```typescript theme={null}
// Query by ID (throws if not found)
const user = await UserModel.findById("A", 1);
```

### findOne

* Fetches single record by **conditions**
* **Returns null** if record not found
* Supports complex conditions
* **Must be implemented manually** (optional)

```typescript theme={null}
// Query by conditions (returns null if not found)
const user = await UserModel.findOne("A", {
  email: "user@example.com",
});
```

### findMany

* Fetches multiple records by **conditions**
* Supports **pagination**
* Returns **total count**
* **Auto-generated**

```typescript theme={null}
// List query (with pagination)
const { rows, total } = await UserModel.findMany("A", {
  status: "active",
  num: 20,
  page: 1,
});
```

## Type Safety

Return types are automatically inferred based on the subset:

```typescript theme={null}
// Subset A: basic fields only
const userA = await UserModel.findById("A", 1);
userA.posts; // ❌ Type error: Property 'posts' does not exist

// Subset B: includes posts
const userB = await UserModel.findById("B", 1);
userB.posts; // ✅ Post[]
```

## Client Usage

### React (TanStack Query)

```typescript theme={null}
import { useQuery } from "@tanstack/react-query";
import { UserService } from "@/services/UserService";

function UserProfile({ userId }: { userId: number }) {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ["user", userId],
    queryFn: () => UserService.getUser("A", userId)
  });

  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>User not found</div>;

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}
```

### Vue

```typescript theme={null}
import { UserService } from "@/services/UserService";

export default {
  data() {
    return {
      user: null,
      loading: false,
      error: null,
    };
  },
  async mounted() {
    this.loading = true;
    try {
      this.user = await UserService.getUser("A", this.userId);
    } catch (error) {
      this.error = error.message;
    } finally {
      this.loading = false;
    }
  },
};
```

## Cautions

### 1. Subset Selection

Choose subsets that include only the necessary data. Loading unnecessary relationships degrades performance.

```typescript theme={null}
// ❌ Bad: loads all relationships
const user = await UserModel.findById("Full", 1);

// ✅ Good: only necessary fields
const user = await UserModel.findById("A", 1);
```

### 2. Exception Handling

`findById` throws an exception if the record doesn't exist. Add exception handling when necessary.

```typescript theme={null}
// ❌ Bad: no exception handling
const user = await UserModel.findById("A", userId); // Can throw 404 error

// ✅ Good: with exception handling
try {
  const user = await UserModel.findById("A", userId);
} catch (error) {
  if (error instanceof NotFoundException) {
    // Handle appropriately
  }
}
```

### 3. Null Possibility

If record existence is uncertain, use `findOne` (requires manual implementation).

```typescript theme={null}
// findById: record must exist
const user = await UserModel.findById("A", 1);

// findOne: record may not exist (requires manual implementation)
const user = await UserModel.findOne("A", { email: "unknown@example.com" });
if (user === null) {
  // Handle missing record
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="findMany" icon="list" href="/en/api-reference/base-model/find-many">
    List queries and pagination
  </Card>

  <Card title="save" icon="floppy-disk" href="/en/api-reference/base-model/save">
    Save and update records
  </Card>

  <Card title="Subset" icon="layer-group" href="/en/database/subsets/what-are-subsets">
    Understanding subsets
  </Card>

  <Card title="Exception Handling" icon="triangle-exclamation" href="/en/api-reference/exceptions/not-found-error">
    Handling NotFoundException
  </Card>
</CardGroup>
