# HTTP Request and Response Handling

The Interface API provides the `HTTPResult` class and `RequestContext` interface for handling HTTP requests and generating responses.

## The `HTTPResult` class

The `HTTPResult` class represents an HTTP response. It encapsulates the status code, body, content type, custom headers, and optional streaming.

### Basic usage

Route handlers can return plain objects, strings, or `HTTPResult` instances.

```typescript
import { Controller, Get, Post, HTTPResult } from "@antelopejs/interface-api";

class ExampleController extends Controller("/examples") {
  @Get()
  getExample() {
    // Returns JSON (content-type: application/json)
    return { status: "success", data: [1, 2, 3] };
  }

  @Get("text")
  getTextExample() {
    // Returns plain text (content-type: text/plain)
    return "This is a text response";
  }

  @Get("custom")
  getCustom() {
    // Returns a fully customized response
    const result = new HTTPResult(200, "<h1>Hello World</h1>", "text/html");
    result.addHeader("X-Custom-Header", "Custom Value");
    return result;
  }
}
```

### Status codes

Set HTTP status codes through the constructor or the `setStatus` method.

```typescript
// Through the constructor
const notFound = new HTTPResult(404, "Not Found");

// Through setStatus
const forbidden = new HTTPResult();
forbidden.setStatus(403);
```

### Response body

The body accepts strings, objects, or a custom content type.

```typescript
// String body (text/plain)
const textResult = new HTTPResult(200, "Hello World");

// Object body (application/json)
const jsonResult = new HTTPResult(200, { message: "Success" });

// Custom content type
const htmlResult = new HTTPResult(200, "<p>HTML content</p>", "text/html");
```

When the body is an object, `HTTPResult` automatically serializes it to JSON and sets the content type to `application/json`.

### Custom headers

Add, remove, and retrieve custom response headers.

```typescript
const result = new HTTPResult(200, "Success");

// Add headers
result.addHeader("X-Rate-Limit", "100");
result.addHeader("X-Rate-Limit-Remaining", "99");

// Remove a header
result.removeHeader("X-Rate-Limit");

// Get all headers
const headers = result.getHeaders();

// Read headers without creating the mutable store
const existingHeaders = result.peekHeaders();
```

### The `withHeaders` static method

The `HTTPResult.withHeaders` method creates an `HTTPResult` with additional headers from an existing body or `HTTPResult`.

```typescript
import { HTTPResult } from "@antelopejs/interface-api";

// From a plain body
const result = HTTPResult.withHeaders(
  { message: "Success" },
  { "X-Request-Id": "abc-123" }
);

// From an existing HTTPResult
const original = new HTTPResult(200, { data: "test" });
const withExtra = HTTPResult.withHeaders(original, {
  "Cache-Control": "no-cache",
});
```

### Streaming responses

For long-running processes or server-sent events, use the `getWriteStream` method.

```typescript
import { Controller, Get, Context, WriteStream, RequestContext } from "@antelopejs/interface-api";
import { PassThrough } from "node:stream";

class StreamController extends Controller("/stream") {
  @Get()
  async streamData(
    @Context() context: RequestContext,
    @WriteStream() stream: PassThrough,
  ) {
    stream.write("Starting stream\n");

    for (let i = 0; i < 10; i++) {
      await new Promise((resolve) => setTimeout(resolve, 1000));
      stream.write(`Data chunk ${i}\n`);
    }

    stream.end("Stream complete\n");
    return context.response;
  }
}
```

The `getWriteStream` method accepts an optional content type (defaults to `text/plain`) and an optional status code (defaults to `200`).

## The `RequestContext` interface

The `RequestContext` interface provides access to all request-related information.

| Property          | Type                         | Description                                  |
| ----------------- | ---------------------------- | -------------------------------------------- |
| `rawRequest`      | `IncomingMessage`            | The raw Node.js HTTP request object          |
| `rawResponse`     | `ServerResponse`             | The raw Node.js HTTP response object         |
| `url`             | `URL`                        | The parsed request URL                       |
| `routeParameters` | `Record<string, string>`     | Parameters extracted from URL path segments  |
| `body`            | `unknown`                    | The request body data                        |
| `response`        | `HTTPResult`                 | The response object sent to the client       |
| `error`           | `unknown`                    | Error thrown during processing, if any        |
| `connection`      | `unknown`                    | WebSocket connection, if applicable          |

## Read request bodies

The `ReadBody` function reads the raw request body as a `Buffer`. Buffered request bodies are limited to 1 MiB by default. Requests with a larger `Content-Length`, or whose received data exceeds the limit, receive HTTP 413 Payload Too Large.

```typescript
import { Controller, Post, RawBody } from "@antelopejs/interface-api";

class UserController extends Controller("/users") {
  @Post()
  async createUser(@RawBody() body: Buffer) {
    const data = JSON.parse(body.toString());
    return { id: "new-user", ...data };
  }
}
```

For JSON bodies, use the `@JSONBody` decorator, which automatically parses the request body.

```typescript
import { Controller, Post, JSONBody } from "@antelopejs/interface-api";

class UserController extends Controller("/users") {
  @Post()
  async createUser(@JSONBody() userData: { name: string; email: string }) {
    return { id: "new-user-id", ...userData };
  }
}
```

Pass a byte limit to `@RawBody`, `@JSONBody`, or `ReadBody` when an endpoint intentionally accepts a larger buffered body:

```typescript
const TEN_MIBIBYTES = 10 * 1024 * 1024;

class UploadController extends Controller("/uploads") {
  @Post()
  async upload(@RawBody(TEN_MIBIBYTES) body: Buffer) {
    return { bytes: body.length };
  }
}
```

For bodies that should not be buffered in memory, use `@Context()` and consume `context.rawRequest` as a stream instead.
