# AI Guide For @hile/http



<!-- Generated by scripts/build-ai-context.mjs from docs/ai. Do not edit by hand. -->



Purpose: Build Koa HTTP APIs with file-system controllers, middleware, response plugins, and Zod validation.



Use this file when an AI agent installs the npm package and needs package-local examples, package selection rules, boundaries, and verification steps.



## Package Selection



| User asks for | Use | Also read |
|---|---|---|
| Create an HTTP endpoint or Koa middleware | `@hile/http` | `packages/http.md`, `recipes/http-api-model-typeorm.md` |
| Run Next.js and API controllers on one port | `@hile/http-next` | `packages/http-next.md`, `recipes/http-next-fullstack.md` |



# HTTP And File Routes

Packages: `@hile/http`, `@hile/loader`.

## Use When

Use `@hile/http` for Koa-based APIs, middleware, manual routes, file-system controllers, Zod validation, and response plugins.

## Do Not Use When

- Do not use it for Next.js pages; use `@hile/http-next` when Next.js and API routes share a port.
- Do not assume Zod validation mutates/coerces Koa context data. It validates only.

## Install

```bash
pnpm add @hile/http
```

## Imports

```ts
import { Http, defineController, createControllerMetadata, defineResponsePlugin } from '@hile/http'
import { z } from 'zod'
```

## Copy-Paste Example

```ts
import { defineController } from '@hile/http'

export default defineController('GET', async () => {
  return { ok: true }
})
```

File route examples:

```text
src/controllers/index.controller.ts -> /
src/controllers/users/index.controller.ts -> /users
src/controllers/users/[id].controller.ts -> /users/:id
```

## More Examples

```ts
import { z } from 'zod'
import { createControllerMetadata, defineController } from '@hile/http'

const bodySchema = z.object({
  username: z.string().min(1),
  age: z.number().int().nonnegative(),
})

export default defineController(
  createControllerMetadata({
    method: 'POST',
    schema: { body: bodySchema },
  }),
  async (ctx) => {
    const body = bodySchema.parse(ctx.request.body)
    return { created: true, user: body }
  },
)
```

The second parse is intentional when you need parsed/coerced data. Hile's controller validation does not write `safeParse().data` back to `ctx.request.body`.

## Compose With

- Call `loadModel()` from controllers to keep business logic out of HTTP files.
- Use `contextHttp()` before controllers to seed request context.
- Use `rateLimitHttp()` for Redis-backed HTTP quotas.

## Runtime And Lifecycle Notes

- `new Http({ port })` creates a Koa app and a `find-my-way` router.
- `http.use(middleware)` registers Koa middleware before `listen()`.
- `http.listen()` returns a close function.
- `http.load(directory, options)` scans `*.controller.{ts,js,tsx,jsx,mjs}` by suffix.
- `defineController()` supports method-only, method-plus-middlewares, and metadata-plus-Zod forms.
- Response plugins transform handler return values and set `ctx.body` when the final result is not `undefined`.

## Anti-Patterns

- Setting `ctx.body` and returning a value from the same controller.
- Loading controllers after starting only because an old example does it; prefer load before listen in new code.
- Using old validation examples that assume Zod coercion rewrote `ctx.query`.

## Verification Checklist

- Controller files default-export `defineController(...)` or an array of controllers.
- Controllers return response values.
- Boot service awaits `http.load()` before `http.listen()`.
- Zod schemas are used for validation, and parsed data is explicitly parsed when needed.



# Related Recipes



# HTTP API + Model + TypeORM

## Complete Example

Controller:

```ts
// src/controllers/users/[id].controller.ts
import { defineController } from '@hile/http'
import { loadModel } from '@hile/model'
import { getUser } from '../../models/users/get-user.model'

export default defineController('GET', async (ctx) => {
  return loadModel(getUser, { userId: String(ctx.params.id) })
})
```

Model:

```ts
// src/models/users/get-user.model.ts
import { defineModel } from '@hile/model'
import typeormService from '@hile/typeorm'
import { User } from '../../entities/user.entity'

export const getUser = defineModel({
  services: [typeormService] as const,
  async main([ds], input: { userId: string }) {
    const user = await ds.getRepository(User).findOneBy({ id: input.userId })
    if (!user) return { found: false }
    return { found: true, user }
  },
})
```

Boot file:

```ts
// src/services/http.boot.ts
import { defineService } from '@hile/core'
import { Http } from '@hile/http'

export default defineService('http', async (shutdown) => {
  const http = new Http({ port: Number(process.env.HTTP_PORT ?? 3000) })
  await http.load(new URL('../controllers', import.meta.url).pathname)
  const close = await http.listen()
  shutdown(close)
  return http
})
```

Package config:

```json
{
  "type": "module",
  "scripts": {
    "dev": "hile start --dev --env-file .env",
    "start": "hile start --env-file .env.prod"
  },
  "hile": {
    "auto_load_packages": ["@hile/typeorm"]
  }
}
```

## File Layout

```text
src/
  controllers/users/[id].controller.ts
  entities/user.entity.ts
  models/users/get-user.model.ts
  services/http.boot.ts
```

## User Intent

Use this recipe when the user wants an HTTP endpoint backed by reusable business logic and a SQL database.

## Packages To Use

- `@hile/http`
- `@hile/model`
- `@hile/typeorm`
- `@hile/core`

## Implementation Steps

1. Put domain logic in a model file.
2. Inject TypeORM through `services: [typeormService] as const`.
3. Keep the controller thin and call `loadModel(model, objectInput)`.
4. Load controllers before `http.listen()`.
5. Register the close function with `shutdown()`.

## Failure And Cleanup Behavior

- TypeORM default service destroys the DataSource during container shutdown.
- Controller errors propagate through Koa.
- If writes are involved, wrap them in `transaction(ds, async (runner, rollback) => ...)`.

## Verification Checklist

- Controller returns `loadModel(...)`.
- Model input is an object.
- Boot file default-exports `defineService(...)`.
- `@hile/typeorm` is auto-loaded or explicitly loaded before use.



# Global Guardrails



## Never Generate These Patterns

- Do not call `loadService()` at module top level; it starts resources during import.
- Do not default-export plain functions from `*.boot.*` files; `hile start` expects a Hile service.
- Do not set `ctx.body` and also return a controller value.
- Do not assume `@hile/http` Zod validation mutates or coerces `ctx.query`, `ctx.params`, or `ctx.request.body`.
- Do not put reusable business logic only in controllers, pages, queue workers, or message handlers.
- Do not use old message examples that append a secondary response getter; current request APIs return promises directly.
- Do not claim exactly-once delivery or execution from Redis locks, queues, idempotency, or rate limits.
- Do not use queue `jobId` as the only side-effect idempotency boundary.
- Do not log the entire async context by default.
