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

# API Request & Response

> Types for HTTP API functions and streaming requests

The III SDK provides types for handling HTTP API requests and responses, including support for streaming data.

## ApiRequest

Represents an incoming HTTP request with parsed parameters and body.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    type ApiRequest<TBody = unknown> = {
      path_params: Record<string, string>
      query_params: Record<string, string | string[]>
      body: TBody
      headers: Record<string, string | string[]>
      method: string
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    class ApiRequest(BaseModel, Generic[TInput]):
        path_params: dict[str, str] = Field(default_factory=dict, alias="pathParams")
        query_params: dict[str, str | list[str]] = Field(default_factory=dict, alias="queryParams")
        body: Any | None = None
        headers: dict[str, str | list[str]] = Field(default_factory=dict)
        method: str = "GET"
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    pub struct ApiRequest<T = Value> {
        pub query_params: HashMap<String, String>,
        pub path_params: HashMap<String, String>,
        pub headers: HashMap<String, String>,
        pub path: String,
        pub method: String,
        pub body: T,
    }
    ```
  </Tab>
</Tabs>

### Fields

<ResponseField name="path_params" type="object" required>
  Path parameters extracted from the URL pattern (e.g., `/users/:id` → `{ id: "123" }`).
</ResponseField>

<ResponseField name="query_params" type="object" required>
  Query string parameters. Values can be strings or arrays for repeated parameters.
</ResponseField>

<ResponseField name="body" type="TBody" required>
  The parsed request body. Type can be specified via generic parameter.
</ResponseField>

<ResponseField name="headers" type="object" required>
  HTTP headers. Values can be strings or arrays for repeated headers.
</ResponseField>

<ResponseField name="method" type="string" required>
  HTTP method (GET, POST, PUT, PATCH, DELETE, etc.).
</ResponseField>

## ApiResponse

Represents an HTTP response to be sent back to the client.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    type ApiResponse<
      TStatus extends number = number,
      TBody = string | Buffer | Record<string, unknown>
    > = {
      status_code: TStatus
      headers?: Record<string, string>
      body?: TBody
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    class ApiResponse(BaseModel, Generic[TOutput]):
        status_code: int = Field(alias="statusCode")
        body: Any
        headers: dict[str, str] = Field(default_factory=dict)
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    pub struct ApiResponse<T = Value> {
        pub status_code: u16,
        pub headers: HashMap<String, String>,
        pub body: T,
    }
    ```
  </Tab>
</Tabs>

### Fields

<ResponseField name="status_code" type="number" required>
  HTTP status code (200, 404, 500, etc.).
</ResponseField>

<ResponseField name="headers" type="object">
  Response headers to include.
</ResponseField>

<ResponseField name="body" type="TBody">
  Response body. Can be a string, Buffer, or object (automatically JSON serialized).
</ResponseField>

## HttpRequest

For streaming HTTP handlers, includes access to the request body stream.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    type HttpRequest<TBody = unknown> = {
      path_params: Record<string, string>
      query_params: Record<string, string | string[]>
      body: TBody
      headers: Record<string, string | string[]>
      method: string
      request_body: ChannelReader
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    @dataclass
    class HttpRequest:
        path_params: dict[str, str]
        query_params: dict[str, str | list[str]]
        body: Any
        headers: dict[str, str | list[str]]
        method: str
        request_body: ChannelReader
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // HttpRequest in Rust is the same as ApiRequest
    // For streaming, use InternalHttpRequest which includes:
    pub struct InternalHttpRequest<TBody = Value> {
        pub path_params: HashMap<String, String>,
        pub query_params: HashMap<String, String>,
        pub body: TBody,
        pub headers: HashMap<String, String>,
        pub method: String,
        pub response: ChannelWriter,
        pub request_body: ChannelReader,
    }
    ```
  </Tab>
</Tabs>

### Additional Fields

<ResponseField name="request_body" type="ChannelReader" required>
  Stream reader for accessing the raw request body as chunks.
</ResponseField>

## HttpResponse

Streaming response writer for HTTP handlers.

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    type HttpResponse = {
      status: (statusCode: number) => void
      headers: (headers: Record<string, string>) => void
      stream: NodeJS.WritableStream
      close: () => void
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    class HttpResponse:
        async def status(self, status_code: int) -> None
        async def headers(self, headers: dict[str, str]) -> None
        @property
        def stream(self) -> WritableStream
        def close(self) -> None
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // HttpResponse functionality is provided through ChannelWriter
    // Send control messages via writer.send_message()
    ```
  </Tab>
</Tabs>

### Methods

<ResponseField name="status" type="function" required>
  Set the HTTP status code for the response.
</ResponseField>

<ResponseField name="headers" type="function" required>
  Set response headers.
</ResponseField>

<ResponseField name="stream" type="WritableStream" required>
  Writable stream for sending response body chunks.
</ResponseField>

<ResponseField name="close" type="function" required>
  Close the response stream and complete the HTTP response.
</ResponseField>

## Usage Examples

### Simple API Handler

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    type CreateUserRequest = {
      name: string
      email: string
    }

    iii.registerFunction(
      { id: 'api::users::create' },
      async (req: ApiRequest<CreateUserRequest>): Promise<ApiResponse<201, { id: string }>> => {
        const user = await db.users.create(req.body)
        
        return {
          status_code: 201,
          headers: { 'Content-Type': 'application/json' },
          body: { id: user.id }
        }
      }
    )
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    @dataclass
    class CreateUserRequest:
        name: str
        email: str

    async def create_user(req: ApiRequest[CreateUserRequest]) -> ApiResponse:
        user = await db.users.create(req.body)
        
        return ApiResponse(
            status_code=201,
            headers={'Content-Type': 'application/json'},
            body={'id': user.id}
        )

    iii.register_function('api::users::create', create_user)
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    iii.register_function("api::users::create", |input: Value| {
        Box::pin(async move {
            let req: ApiRequest = serde_json::from_value(input)?;
            let user = db.users.create(req.body).await?;
            
            let response = ApiResponse {
                status_code: 201,
                headers: HashMap::from([
                    ("Content-Type".into(), "application/json".into())
                ]),
                body: json!({ "id": user.id }),
            };
            
            Ok(serde_json::to_value(response)?)
        })
    });
    ```
  </Tab>
</Tabs>

### Streaming Response

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    import { http } from '@iii/sdk'

    const handler = http(async (req: HttpRequest, res: HttpResponse) => {
      await res.status(200)
      await res.headers({ 'Content-Type': 'text/plain' })
      
      res.stream.write('Starting stream...\n')
      
      for (let i = 0; i < 10; i++) {
        await new Promise(resolve => setTimeout(resolve, 100))
        res.stream.write(`Chunk ${i}\n`)
      }
      
      res.close()
    })

    iii.registerFunction({ id: 'api::stream' }, handler)
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    from iii import http

    async def stream_handler(req: HttpRequest, res: HttpResponse):
        await res.status(200)
        await res.headers({'Content-Type': 'text/plain'})
        
        res.stream.write(b'Starting stream...\n')
        
        for i in range(10):
            await asyncio.sleep(0.1)
            res.stream.write(f'Chunk {i}\n'.encode())
        
        res.close()

    iii.register_function('api::stream', http(stream_handler))
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={null}
    // Streaming in Rust uses ChannelWriter directly
    iii.register_function("api::stream", |input: Value| {
        Box::pin(async move {
            let req: InternalHttpRequest = serde_json::from_value(input)?;
            
            req.response.send_message(
                &json!({"type": "set_status", "status_code": 200}).to_string()
            ).await?;
            
            req.response.send_message(
                &json!({"type": "set_headers", "headers": {"Content-Type": "text/plain"}}).to_string()
            ).await?;
            
            req.response.write(b"Starting stream...\n").await?;
            
            for i in 0..10 {
                tokio::time::sleep(Duration::from_millis(100)).await;
                req.response.write(format!("Chunk {}\n", i).as_bytes()).await?;
            }
            
            req.response.close().await?;
            Ok(Value::Null)
        })
    });
    ```
  </Tab>
</Tabs>

## Related

* [Triggers Concepts](/concepts/triggers)
* [Channel Types](/api/types/channel-types)
* [Functions Concepts](/concepts/functions)
