# HTTP Server

The HTTP server modules are the low-level primitives for serving HTTP in Effect.
You describe routes as **values** with `HttpRouter`, return immutable
`HttpServerResponse` values from handlers, and run the whole thing on a concrete
`HttpServer` provided by a platform adapter (Node, Bun, Deno) — or convert it to a
Fetch-compatible web handler for serverless environments.

These same primitives power the higher-level [HTTP API](https://effect.plants.sh/http-api/) layer:
`HttpApiBuilder` ultimately registers routes into an `HttpRouter` and serves them
through an `HttpServer`. Reach for `HttpApi` when you want a schema-first,
fully-typed API with a generated client; reach for these primitives directly when
you want raw routing control, webhooks, health checks, file serving, or a custom
Fetch handler.
**Unstable module:** These modules live under `effect/unstable/http` and are not yet covered by
  semver stability guarantees. Import them as a namespace:

  ```ts
  import { HttpRouter, HttpServer, HttpServerResponse } from "effect/unstable/http"
  ```

  Platform adapters (e.g. `NodeHttpServer`) come from the platform package, such
  as `@effect/platform-node`.

## Mental model

An `HttpRouter` is built up through **layers**: route layers (`HttpRouter.add`,
`HttpRouter.addAll`, `HttpRouter.use`) register `Route` values into the current
router while the application `Layer` is being constructed. Nothing handles a
request immediately. Then one of three entry points reads the completed router and
runs the matching handler per request:

- `HttpRouter.serve(appLayer)` — produces a server `Layer` you run with a platform
  `HttpServer`.
- `HttpRouter.toWebHandler(appLayer)` — produces a `(request: Request) => Promise<Response>`
  Fetch handler plus a `dispose` function.
- `HttpRouter.toHttpEffect(appLayer)` — produces the raw per-request handler effect.

During a request the router provides `HttpServerRequest`, a `Scope`, parsed search
parameters, and `RouteContext` (captured path params), so handlers can decode the
request and access services supplied by middleware.

## A complete minimal example

This serves two routes on Node. Notice that routes are `Layer` values merged
together, and the response constructors (`HttpServerResponse.text` / `.json`) are
plain immutable values — `json` returns an `Effect` because serialization can fail.

```ts
import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
import { Effect, Layer } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"
import { createServer } from "node:http"

// A static-response route: the handler is just an Effect that produces a response.
const HelloRoute = HttpRouter.add(
  "GET",
  "/hello",
  Effect.succeed(HttpServerResponse.text("Hello, World!"))
)

// A route whose handler reads the matched path parameters via RouteContext.
const GreetRoute = HttpRouter.add(
  "GET",
  "/greet/:name",
  Effect.gen(function* () {
    const params = yield* HttpRouter.params
    // HttpServerResponse.json returns an Effect (JSON.stringify may throw)
    return yield* HttpServerResponse.json({ hello: params.name })
  })
)

// Merge the route layers into the application layer.
const AppLayer = Layer.mergeAll(HelloRoute, GreetRoute)

// Turn the app layer into a server Layer, then provide a platform HttpServer.
const ServerLayer = HttpRouter.serve(AppLayer).pipe(
  Layer.provide(NodeHttpServer.layer(createServer, { port: 3000 }))
)

// Launch it. `HttpRouter.serve` already logs the listening address.
Layer.launch(ServerLayer).pipe(NodeRuntime.runMain)
// => GET http://localhost:3000/hello       -> "Hello, World!"
// => GET http://localhost:3000/greet/Alice -> {"hello":"Alice"}
```

The exact same `AppLayer` can become a serverless Fetch handler instead of a
listening server:

```ts
import { HttpRouter, HttpServer } from "effect/unstable/http"

const { handler, dispose } = HttpRouter.toWebHandler(
  // toWebHandler needs the HTTP platform services (Path, FileSystem, etc.)
  AppLayer.pipe(Layer.provide(HttpServer.layerServices))
)

// handler: (request: Request) => Promise<Response>
const response = await handler(new Request("http://localhost/hello"))
// => Response with body "Hello, World!"

await dispose() // release layer resources
```

## The grab bag

The handful of things you reach for most when building a server. Each one has a
full reference on the sub-pages linked below; this is the shortlist.

### Register routes — statically or imperatively

```ts
import { Effect } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"

// One route at a time. The handler is an Effect producing a response.
const Ping = HttpRouter.add("GET", "/ping", Effect.succeed(HttpServerResponse.text("pong")))

// Many at once, under a shared prefix.
const Api = HttpRouter.addAll([
  HttpRouter.route("GET", "/health", Effect.succeed(HttpServerResponse.empty())),
  HttpRouter.route("GET", "/version", Effect.succeed(HttpServerResponse.text("1.0.0")))
], { prefix: "/api" })
// => GET /api/health (204), GET /api/version ("1.0.0")

// Imperatively, when registration depends on other services.
const Dynamic = HttpRouter.use((router) =>
  Effect.gen(function* () {
    yield* router.add("GET", "/a", Effect.succeed(HttpServerResponse.text("a")))
    yield* router.add("GET", "/b", Effect.succeed(HttpServerResponse.text("b")))
  })
)
```

### Decode path & query params with a `Schema`

```ts
import { Effect, Schema } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"

const Params = Schema.Struct({ id: Schema.NumberFromString, page: Schema.NumberFromString })

HttpRouter.add(
  "GET",
  "/items/:id",
  Effect.gen(function* () {
    // schemaParams decodes path + search params together (path wins on conflict)
    const { id, page } = yield* HttpRouter.schemaParams(Params)
    return HttpServerResponse.text(`item ${id} page ${page}`) // both are numbers
  })
)
// => GET /items/42?page=2 -> "item 42 page 2"
```

### Decode a JSON body with a `Schema`

```ts
import { Effect, Schema } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"

const CreateUser = Schema.Struct({
  body: Schema.Struct({ name: Schema.String, age: Schema.Number })
})

HttpRouter.add(
  "POST",
  "/users",
  Effect.gen(function* () {
    // schemaJson decodes the whole request, including the parsed JSON body
    const { body } = yield* HttpRouter.schemaJson(CreateUser)
    return yield* HttpServerResponse.json({ created: body.name })
  })
)
```

### Build responses

```ts
import { HttpServerResponse } from "effect/unstable/http"

HttpServerResponse.text("hello")          // => 200 text/plain
HttpServerResponse.json({ ok: true })     // => Effect -> 200 {"ok":true} (stringify may fail)
HttpServerResponse.empty({ status: 201 }) // => 201 with no body
HttpServerResponse.redirect("/login")     // => 302, Location: /login

// Combinators return a NEW response — pipe to layer on status, headers, cookies.
HttpServerResponse.text("nope").pipe(
  HttpServerResponse.setStatus(404),
  HttpServerResponse.setHeader("x-trace", "abc"),
  HttpServerResponse.setCookieUnsafe("session", "abc123", { httpOnly: true })
)
// => 404, header x-trace + Set-Cookie: session=abc123; HttpOnly
```

### Add CORS, logging, or request-scoped services with middleware

```ts
import { Context, Effect, Layer } from "effect"
import { HttpRouter, HttpServerResponse } from "effect/unstable/http"

// Apply CORS headers to every route.
const Cors = HttpRouter.cors({
  allowedOrigins: ["https://example.com"],
  allowedMethods: ["GET", "POST"]
})

// Provide a request-scoped service that handlers can then `yield*`.
class CurrentUser extends Context.Service<CurrentUser, { id: string }>()("CurrentUser") {}

const WithUser = HttpRouter.middleware<{ provides: CurrentUser }>()(
  Effect.succeed((httpEffect) =>
    Effect.provideService(httpEffect, CurrentUser, { id: "u1" })
  )
).layer
// => Layers you provide to routes; see /http-server/server-middleware/ for depth
```
**Tip:** Want a fully-typed, schema-first API with OpenAPI and a generated client?
  See the [HTTP API](https://effect.plants.sh/http-api/) section, which builds on these primitives. For
  the underlying platform services (`FileSystem`, `Path`, etc.) used by file
  responses, see [Platform](https://effect.plants.sh/platform/).

## In this section

- **[Routing](https://effect.plants.sh/http-server/routing/)** — define routes, HTTP methods, path params,
  prefixes, and schema-decoded inputs with `HttpRouter`; serve on a platform server
  or as a Web `fetch` handler. Full `HttpRouter`, `HttpServer`, `HttpEffect`, and
  server-error reference.
- **[Request & Response](https://effect.plants.sh/http-server/request-and-response/)** — read the incoming
  request (bodies, headers, cookies, search params) and build responses, plus the
  `HttpBody`, `Headers`, `UrlParams`, `Url`, and `Cookies` reference.
- **[Server middleware](https://effect.plants.sh/http-server/server-middleware/)** — wrap handlers for CORS,
  logging, tracing, and request-scoped services with `HttpMiddleware` and
  `HttpRouter.middleware`.
- **[Static files](https://effect.plants.sh/http-server/static-files/)** — serve directories and one-off
  files from disk, generate ETags, and receive multipart uploads.