> ## 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.

# Context Methods

> Context Access Methods Reference

Sonamu provides multiple methods to access the Context. These methods are useful when you need Context outside of API methods.

## Sonamu.getContext()

Returns the Context of the currently executing request. Managed through AsyncLocalStorage, so it can be called anywhere within the same request stack.

### Signature

```typescript theme={null}
Sonamu.getContext(): Context
```

### Return Value

* **Context**: The Context object of the current request
* **Test Environment**: Empty Context object (request and reply are null)

### Exceptions

* **Error**: When Context cannot be found (production environment only)

### Usage Examples

#### Using in Helper Functions

```typescript theme={null}
// src/utils/auth-helper.ts
import { Sonamu } from "sonamu";

export function getCurrentUser() {
  const ctx = Sonamu.getContext();
  return ctx.user;
}

export function requireAuth() {
  const user = getCurrentUser();
  if (!user) {
    throw new UnauthorizedException("Authentication required");
  }
  return user;
}
```

```typescript theme={null}
// src/models/post.model.ts
import { requireAuth } from "../utils/auth-helper";

class PostModelClass extends BaseModelClass {
  @api()
  async createPost(title: string, content: string) {
    // Access user information through helper function without receiving Context directly
    const user = requireAuth();

    return this.create({
      title,
      content,
      authorId: user.id,
    });
  }
}
```

#### Using in Service Layer

```typescript theme={null}
// src/services/notification.service.ts
import { Sonamu } from "sonamu";

export class NotificationService {
  async sendNotification(userId: number, message: string) {
    const ctx = Sonamu.getContext();

    // Send localized message using Context's locale information
    const localizedMessage = this.localize(message, ctx.locale);

    // Notification sending logic
    await this.send(userId, localizedMessage);
  }

  private localize(message: string, locale?: string) {
    // Localization logic
    return message;
  }
}
```

### Test Environment Behavior

In test environments, returns an empty Context without throwing errors even when Context is not injected:

```typescript theme={null}
// test/post.test.ts
import { Sonamu } from "sonamu";

describe("PostModel", () => {
  it("should create post", async () => {
    // Works in test environment without Context
    const ctx = Sonamu.getContext();
    console.log(ctx.request); // null (no error)
  });
});
```

For tests that need actual Context, use `runWithMockContext`, or `testAs` when you need to specify user info:

```typescript theme={null}
import { runWithMockContext, testAs } from "sonamu/test";

it("should use mock context in test", async () => {
  await runWithMockContext(async () => {
    const ctx = Sonamu.getContext();
    expect(ctx.user).toBeNull();
  });
});

testAs(
  { id: 1, email: "test@example.com" },
  "should use context with user",
  async () => {
    const ctx = Sonamu.getContext();
    expect(ctx.user?.id).toBe(1);
  },
);
```

## File Upload Context

For file upload requests, uploaded file information is accessed through `Sonamu.getContext()`. Available only in methods with the `@upload` decorator.

Two properties are used depending on the upload mode:

* **Buffer mode (default)**: `bufferedFiles` - files loaded in memory, suitable for MD5 calculation/image processing
* **Stream mode**: `uploadedFiles` - files streamed directly to storage, suitable for large files

### Signature

```typescript theme={null}
// Buffer mode (default)
const { bufferedFiles } = Sonamu.getContext();
// bufferedFiles: BufferedFile[]

// Stream mode
const { uploadedFiles } = Sonamu.getContext();
// uploadedFiles: UploadedFile[]
```

### Type

```typescript theme={null}
// File upload properties of Context
bufferedFiles?: BufferedFile[];  // buffer mode
uploadedFiles?: UploadedFile[];  // stream mode
```

### Notes

* File properties are only set when using the `@upload` decorator
* In buffer mode (default), use `bufferedFiles`; in stream mode, use `uploadedFiles`
* For single file uploads, access via `bufferedFiles?.[0]` or `uploadedFiles?.[0]`

### Usage Examples

#### Single File Upload (Buffer Mode)

```typescript theme={null}
class FileModelClass extends BaseModelClass {
  @upload()
  async uploadAvatar(ctx: Context) {
    const { bufferedFiles } = Sonamu.getContext();
    const file = bufferedFiles?.[0]; // Use first file

    if (!file) {
      throw new BadRequestException("File is required");
    }

    // Check file information
    console.log(file.filename); // "avatar.jpg"
    console.log(file.mimetype); // "image/jpeg"
    console.log(file.size); // 102400 (bytes)
    console.log(file.extname); // "jpg"

    // Save file (diskName, key order)
    const key = `avatars/${Date.now()}_${file.filename}`;
    const url = await file.saveToDisk("fs", key);

    return { url, filename: file.filename, size: file.size };
  }
}
```

#### Multiple File Upload (Buffer Mode)

```typescript theme={null}
class FileModelClass extends BaseModelClass {
  @upload()
  async uploadDocuments(ctx: Context, folderId: number) {
    const { bufferedFiles } = Sonamu.getContext();

    if (!bufferedFiles || bufferedFiles.length === 0) {
      throw new BadRequestException("At least one file is required");
    }

    const savedFiles = await Promise.all(
      bufferedFiles.map(async (file, i) => {
        const key = `documents/${Date.now()}_${i}_${file.filename}`;
        const url = await file.saveToDisk("fs", key);
        return { url, filename: file.filename, size: file.size };
      }),
    );

    // Save file metadata to database
    await this.createMany(
      savedFiles.map((file) => ({
        folderId,
        filename: file.filename,
        url: file.url,
        size: file.size,
      })),
    );

    return { count: savedFiles.length, files: savedFiles };
  }
}
```

#### Large File Upload (Stream Mode)

```typescript theme={null}
class FileModelClass extends BaseModelClass {
  @upload({ consume: "stream", destination: "s3" })
  async uploadLargeFiles(ctx: Context) {
    const { uploadedFiles } = Sonamu.getContext();

    if (!uploadedFiles || uploadedFiles.length === 0) {
      throw new BadRequestException("File is required");
    }

    // Already uploaded to storage
    return {
      files: uploadedFiles.map((file) => ({
        filename: file.filename,
        url: file.url,
        size: file.size,
      })),
    };
  }
}
```

#### Using in Helper Functions

```typescript theme={null}
// src/utils/file-helper.ts
import { Sonamu } from "sonamu";

export function getBufferedFiles() {
  const { bufferedFiles } = Sonamu.getContext();
  return bufferedFiles;
}

export function validateFileTypes(allowedTypes: string[]) {
  const { bufferedFiles } = Sonamu.getContext();

  if (!bufferedFiles) return;

  for (const file of bufferedFiles) {
    if (!allowedTypes.includes(file.mimetype)) {
      throw new BadRequestException(`File type not allowed: ${file.mimetype}`);
    }
  }
}
```

```typescript theme={null}
class FileModelClass extends BaseModelClass {
  @api()
  @upload()
  async uploadImages(ctx: Context) {
    // Allow only image files
    validateFileTypes(["image/jpeg", "image/png", "image/gif", "image/webp"]);

    const files = getBufferedFiles();

    // File processing logic
    // ...
  }
}
```

## Related Types

### BufferedFile (Buffer Mode)

```typescript theme={null}
class BufferedFile {
  /** Original filename */
  get filename(): string;

  /** MIME type */
  get mimetype(): string;

  /** File size (bytes) */
  get size(): number;

  /** Extension (without dot) */
  get extname(): string | false;

  /** File Buffer (getter) */
  get buffer(): Buffer;

  /** Access to original MultipartFile */
  get raw(): MultipartFile;

  /** Calculate MD5 hash */
  async md5(): Promise<string>;

  /**
   * Save file to disk
   * @param diskName Disk name (e.g., 'fs', 's3')
   * @param key Storage path (e.g., 'uploads/avatar.png')
   * @returns URL of saved file
   */
  async saveToDisk(diskName: DriverKey, key: string): Promise<string>;
}
```

### UploadedFile (Stream Mode)

```typescript theme={null}
class UploadedFile {
  /** Original filename */
  get filename(): string;

  /** MIME type */
  get mimetype(): string;

  /** File size (bytes) */
  get size(): number;

  /** Extension (without dot) */
  get extname(): string | false;

  /** URL of the stored file (Unsigned) */
  get url(): string;

  /** URL of the stored file (Signed, with expiration) */
  get signedUrl(): string;

  /** Key in storage */
  get key(): string;

  /** Storage disk name */
  get diskName(): DriverKey;

  /** Download file from storage */
  async download(): Promise<Buffer>;
}
```

## Precautions

### AsyncLocalStorage Stack

`getContext()` works through AsyncLocalStorage, so it must be called within the same asynchronous execution stack:

```typescript theme={null}
// ✅ Correct usage
class PostModelClass extends BaseModelClass {
  @api()
  async createPost(title: string) {
    const user = this.getCurrentUser(); // Within same stack
    return this.create({ title, authorId: user.id });
  }

  private getCurrentUser() {
    return Sonamu.getContext().user;
  }
}

// ❌ Incorrect usage
class PostModelClass extends BaseModelClass {
  @api()
  async createPost(title: string) {
    // setTimeout creates a new execution stack
    setTimeout(() => {
      const ctx = Sonamu.getContext(); // Error!
    }, 1000);
  }
}
```

### @upload Decorator Required

The `bufferedFiles`/`uploadedFiles` properties can only be used in methods with the `@upload` decorator:

```typescript theme={null}
// ✅ Correct usage (Buffer Mode)
@api()
@upload()
async uploadFile() {
  const { bufferedFiles } = Sonamu.getContext();
  const file = bufferedFiles?.[0]; // OK
}

// ✅ Correct usage (Stream Mode)
@api()
@upload({ consume: "stream", destination: "s3" })
async uploadFile() {
  const { uploadedFiles } = Sonamu.getContext();
  const file = uploadedFiles?.[0]; // OK
}

// ❌ Incorrect usage
@api()
async uploadFile() {
  const { bufferedFiles } = Sonamu.getContext();
  // bufferedFiles is undefined
}
```

## Related Documentation

* [Context](/en/api-reference/context/sonamu-context)
* [AuthContext](/en/api-reference/context/auth-context)
* [File Upload](/en/api-development/file-upload)
* [Using Context in Tests](/en/testing/mocking/run-with-mock-context)
