# Middleware & Auth

`HttpApi` middleware is a typed, schema-aware layer that runs around endpoints.
Unlike raw HTTP middleware it is part of the API definition, so it can:

- declare **security schemes** (bearer, API key, basic) that are decoded for you
  and documented in OpenAPI,
- **provide services** (such as the current user) to downstream middleware and
  handlers, and
- declare the **errors** it may raise, which then appear in the endpoint's error
  channel and in the generated client.

Middleware is split into a **definition** (shared with clients) and an
**implementation** (server-only), exactly like the API itself.

## Defining the middleware

`HttpApiMiddleware.Service` declares the middleware. The type parameter object
describes what it `provides` and `requires`; the options describe its `security`
schemes and `error` type.

```ts
// api/Authorization.ts
import { Context, Schema } from "effect"
import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"
import type { User } from "../domain/User.ts"

// The service this middleware injects for downstream handlers to consume
export class CurrentUser extends Context.Service<CurrentUser, User>()(
  "acme/HttpApi/Authorization/CurrentUser"
) {}

// The error this middleware can raise, with its HTTP status
export class Unauthorized extends Schema.TaggedErrorClass<Unauthorized>()(
  "Unauthorized",
  { message: Schema.String },
  { httpApiStatus: 401 }
) {}

export class Authorization extends HttpApiMiddleware.Service<Authorization, {
  // Services made available to endpoints/middleware that run after this one
  provides: CurrentUser
  // Services this middleware needs from earlier middleware (none here)
  requires: never
}>()("acme/HttpApi/Authorization", {
  // Clients must also implement this middleware (e.g. to attach a token)
  requiredForClient: true,
  // Security schemes power OpenAPI docs *and* decode credentials for you
  security: {
    bearer: HttpApiSecurity.bearer
  },
  // Errors this middleware may fail with
  error: Unauthorized
}) {}
```
**Note:** Because the middleware `provides: CurrentUser`, any endpoint guarded by it can
`yield* CurrentUser` (or return it directly) in its handler. The dependency shows
up in the handler's context type — there is no untyped "request locals" bag.

### The `Service` type-parameter object

The first call — `HttpApiMiddleware.Service<Self, Config>()` — accepts an
optional config object with two keys:

| Key | Meaning |
| --- | --- |
| `provides` | Services this middleware makes available to everything that runs after it (other middleware and the endpoint handler). Removed from the handler's requirement type. |
| `requires` | Services this middleware needs in order to run (typically provided by an earlier middleware in the chain). Added to the overall requirement type. |

Both default to `never`. Internally these drive `HttpApiMiddleware.ApplyServices<A, R>`,
which removes `Provides<A>` from a handler's requirements and adds `Requires<A>`.

### The `Service` options object

The second call — `(id, options?)` — takes the middleware id string and an
options object:

| Option | Type | Meaning |
| --- | --- | --- |
| `error` | `Schema.Top \| ReadonlyArray<Schema.Top>` | Error schema(s) this middleware may fail with. They are merged into the endpoint error surface and the generated client, so they must be `Schema` values. Defaults to no errors. |
| `security` | `Record<string, HttpApiSecurity>` | Map of scheme name → `HttpApiSecurity`. Declaring this makes the middleware a **security middleware**: it receives decoded credentials and the server implementation provides one handler per key. Must be non-empty (an empty object throws at definition time). |
| `requiredForClient` | `boolean` | When `true`, the generated client must also install a matching client middleware (via [`layerClient`](#layerclient)). Defaults to `false`. |

```ts
import { Schema } from "effect"
import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"

class RateLimited extends Schema.TaggedErrorClass<RateLimited>()(
  "RateLimited",
  { retryAfter: Schema.Number },
  { httpApiStatus: 429 }
) {}

// A plain (non-security) middleware: no `security`, so no credentials are decoded
export class RateLimit extends HttpApiMiddleware.Service<RateLimit>()(
  "acme/HttpApi/RateLimit",
  { error: RateLimited }
) {}

// A security middleware reading an API key from a header
export class ApiKeyAuth extends HttpApiMiddleware.Service<ApiKeyAuth>()(
  "acme/HttpApi/ApiKeyAuth",
  { security: { apiKey: HttpApiSecurity.apiKey({ key: "x-api-key" }) } }
) {}
```

## Applying middleware

Attach middleware to a whole group with `.middleware(...)`, or to a single
endpoint. Group-level is the common case for auth:

```ts
// api/Users.ts (excerpt)
import { HttpApiGroup } from "effect/unstable/httpapi"
import { Authorization } from "./Authorization.ts"

class UsersApiGroup extends HttpApiGroup.make("users")
  .add(/* ...endpoints... */)
  // Every endpoint in this group now requires authorization, and `CurrentUser`
  // becomes available to each handler.
  .middleware(Authorization)
  .prefix("/users")
{}
```

To guard a single endpoint instead, call `.middleware(...)` on the endpoint
before adding it to the group:

```ts
// Only this endpoint requires Authorization; siblings stay public
HttpApiEndpoint.get("me", "/me")
  .addSuccess(User)
  .middleware(Authorization)
```

## Implementing the middleware (server)

The implementation is a `Layer` providing the `Authorization` service. Because
the definition declared a `bearer` security scheme, the implementation receives
the already-decoded `credential`. Validate it and use `Effect.provideService` to
inject the resolved `CurrentUser` into the continuation (`httpEffect`).

```ts
// server/Authorization.ts
import { Effect, Layer, Redacted } from "effect"
import { Authorization, CurrentUser, Unauthorized } from "../api/Authorization.ts"
import { User, UserId } from "../domain/User.ts"

export const AuthorizationLayer = Layer.effect(
  Authorization,
  Effect.gen(function*() {
    // Resolve middleware dependencies once (a DB, an auth provider, ...)
    yield* Effect.logInfo("Starting Authorization middleware")

    return Authorization.of({
      // One handler per declared security scheme. `credential` is decoded from
      // the Authorization header and wrapped in Redacted so it never logs.
      bearer: Effect.fn(function*(httpEffect, { credential }) {
        const token = Redacted.value(credential)
        if (token !== "dev-token") {
          // Failing here short-circuits the request with a 401
          return yield* new Unauthorized({ message: "Missing or invalid bearer token" })
        }

        // Provide CurrentUser to everything downstream: other middleware and
        // the endpoint handler.
        return yield* Effect.provideService(
          httpEffect,
          CurrentUser,
          new User({ id: UserId.make(1), name: "Dev User", email: "dev@acme.com" })
        )
      })
    })
  })
)
```

The handler from the previous page can now read the injected user:

```ts
// inside HttpApiBuilder.group(Api, "users", ...)
.handle("me", () => CurrentUser) // returns the injected current user
```

A **plain** (non-security) middleware has a single function instead of a
per-scheme record — it receives only `httpEffect` and the endpoint/group
metadata, with no `credential`:

```ts
import { Effect, Layer } from "effect"
import { RateLimit } from "../api/RateLimit.ts"

export const RateLimitLayer = Layer.succeed(
  RateLimit,
  // (httpEffect, { endpoint, group }) => Effect.Effect<HttpServerResponse, ...>
  (httpEffect, { endpoint }) =>
    Effect.tap(httpEffect, () => Effect.logDebug(`served ${endpoint.name}`))
)
```
**Tip:** Credentials use [`Redacted`](https://effect.plants.sh/data-types/redacted/) so the token never appears in
logs or error messages. Call `Redacted.value(...)` only at the point you actually
need the raw string.

## HttpApiSecurity reference

`HttpApiSecurity` describes **where** a credential is read from and **which
credential type** is handed to your security middleware. The schemes do not
authenticate by themselves — they decode the request and feed your middleware
handler, and they drive OpenAPI `securitySchemes` generation.

The union is `HttpApiSecurity = Http | ApiKey | Basic`, and every scheme carries a
decoded credential type recoverable via `HttpApiSecurity.Type<S>`.

### http

A generic HTTP `Authorization` scheme. The credential is decoded by stripping the
scheme prefix from the `Authorization` header and is delivered as a
`Redacted<string>`. `bearer` is just `http({ scheme: "Bearer" })`.

```ts
import { HttpApiSecurity } from "effect/unstable/httpapi"

const githubToken = HttpApiSecurity.http({ scheme: "token" })
// reads `Authorization: token <value>` -> credential: Redacted<string>
// => { _tag: "Http", scheme: "token", ... }
```

### bearer

The standard `Authorization: Bearer <token>` scheme. Pre-built convenience value
equal to `http({ scheme: "Bearer" })`; the credential is a `Redacted<string>`.

```ts
import { HttpApiSecurity } from "effect/unstable/httpapi"

HttpApiSecurity.bearer
// reads `Authorization: Bearer <token>` -> credential: Redacted<string>
// => { _tag: "Http", scheme: "Bearer", ... }
```

### apiKey

An API-key scheme. `key` is the parameter name and `in` selects the source —
`"header"` (default), `"query"`, or `"cookie"`. The credential is a
`Redacted<string>`. For `"header"`, the key is matched case-insensitively (HTTP
header normalization); for `"query"` and `"cookie"` it is matched exactly.

```ts
import { HttpApiSecurity } from "effect/unstable/httpapi"

HttpApiSecurity.apiKey({ key: "x-api-key" })
// in defaults to "header" -> { _tag: "ApiKey", key: "x-api-key", in: "header", ... }

HttpApiSecurity.apiKey({ key: "api_key", in: "query" })
// reads ?api_key=... -> { _tag: "ApiKey", key: "api_key", in: "query", ... }

HttpApiSecurity.apiKey({ key: "session", in: "cookie" })
// reads the `session` cookie -> { _tag: "ApiKey", key: "session", in: "cookie", ... }
```
**Note:** If decoding fails (header/cookie/param missing or invalid), `securityDecode`
yields an **empty** `Redacted("")` rather than failing — so your middleware must
reject empty/invalid credentials explicitly.

### basic

HTTP Basic authentication. Reads `Authorization: Basic <base64>` and decodes it
into a `Credentials` object: `{ username: string; password: Redacted<string> }`.
The username is exposed in plain text; the password is redacted.

```ts
import { HttpApiSecurity } from "effect/unstable/httpapi"

HttpApiSecurity.basic
// reads `Authorization: Basic <base64(user:pass)>`
// -> credential: { username: string, password: Redacted<string> }
// => { _tag: "Basic", ... }
```

### annotate

Adds a single OpenAPI annotation value to a scheme, keyed by a `Context.Key`.
Used to enrich generated docs; it does not change runtime decoding. Dual: pipeable
or data-first.

```ts
import { HttpApiSecurity, OpenApi } from "effect/unstable/httpapi"

const documented = HttpApiSecurity.bearer.pipe(
  HttpApiSecurity.annotate(OpenApi.Description, "JWT access token")
)
// same runtime scheme, now carrying an OpenAPI description annotation
```

### annotateMerge

Merges an entire `Context.Context` of annotations into a scheme at once (instead
of one key/value at a time). Also dual.

```ts
import { Context } from "effect"
import { HttpApiSecurity } from "effect/unstable/httpapi"

declare const annotations: Context.Context<never>

const documented = HttpApiSecurity.annotateMerge(HttpApiSecurity.bearer, annotations)
// bearer scheme with the supplied annotation context merged in
```

### HttpApiSecurity.Type

Type-level helper that extracts the decoded credential type of a scheme. Useful
when typing middleware handlers by hand.

```ts
import type { HttpApiSecurity } from "effect/unstable/httpapi"

type Bearer = HttpApiSecurity.HttpApiSecurity.Type<HttpApiSecurity.Http>
// => Redacted<string>
type Basic = HttpApiSecurity.HttpApiSecurity.Type<HttpApiSecurity.Basic>
// => { readonly username: string; readonly password: Redacted<string> }
```

## HttpApiMiddleware reference

### Service

The constructor for middleware service keys — documented in full above. Returns a
`Context.Service` class augmented with the middleware metadata (provided/required
services, error schemas, security schemes, client requirements) used by groups,
the builder, and generated clients.

```ts
import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"

class Auth extends HttpApiMiddleware.Service<Auth>()("acme/Auth", {
  security: { bearer: HttpApiSecurity.bearer }
}) {}
// => a Context.Service class; HttpApiMiddleware.isSecurity(Auth) === true
```

### isSecurity

Type guard that returns `true` when a middleware service was declared with a
`security` option (and therefore decodes credentials).

```ts
import { HttpApiMiddleware, HttpApiSecurity } from "effect/unstable/httpapi"

class Auth extends HttpApiMiddleware.Service<Auth>()("acme/Auth", {
  security: { bearer: HttpApiSecurity.bearer }
}) {}
class Log extends HttpApiMiddleware.Service<Log>()("acme/Log") {}

HttpApiMiddleware.isSecurity(Auth) // => true
HttpApiMiddleware.isSecurity(Log)  // => false
```

### layerSchemaErrorTransform

Builds a middleware `Layer` that catches `HttpApiSchemaError` failures (raised
while decoding request payloads / encoding responses) and maps them to one of the
middleware's **declared** error schemas. The transform must fail with an error
covered by the middleware's `error` option.

```ts
import { Effect, Schema } from "effect"
import { HttpApiMiddleware } from "effect/unstable/httpapi"

class CustomError extends Schema.TaggedErrorClass<CustomError>()("CustomError", {}) {}

class ErrorHandler extends HttpApiMiddleware.Service<ErrorHandler>()(
  "api/ErrorHandler",
  { error: CustomError }
) {}

export const ErrorHandlerLayer = HttpApiMiddleware.layerSchemaErrorTransform(
  ErrorHandler,
  (schemaError) =>
    Effect.log("Got SchemaError", schemaError).pipe(
      Effect.andThen(Effect.fail(new CustomError()))
    )
)
// => Layer.Layer<ErrorHandler>
```

### layerClient

Provides the **client-side** implementation for a middleware declared with
`requiredForClient: true`. The function receives `{ endpoint, group, request,
next }` and returns the response from `next(...)`; the layer captures its
surrounding services and publishes them through the `ForClient` marker that the
generated client consumes.

```ts
import { Effect } from "effect"
import { HttpClientRequest } from "effect/unstable/http"
import { HttpApiMiddleware } from "effect/unstable/httpapi"
import { Authorization } from "../api/Authorization.ts"

export const AuthorizationClient = HttpApiMiddleware.layerClient(
  Authorization,
  Effect.fn(function*({ next, request }) {
    // Modify the request, then pass it down the chain. Here we attach a token.
    return yield* next(HttpClientRequest.bearerToken(request, "dev-token"))
  })
)
// => Layer.Layer<ForClient<Authorization>>
```

This client layer is provided when constructing the client — see
[Serving & clients](https://effect.plants.sh/http-api/serving-and-clients/) for the full wiring.
**Note:** `layerClient` also accepts an `Effect` that *produces* the middleware function, so
you can resolve dependencies (a token store, a refresh service) before returning
the per-request handler.

### Service models (type-level)

These types are exported for advanced typing and are rarely written by hand, but
they describe the shapes the builder relies on:

| Type | Description |
| --- | --- |
| `HttpApiMiddleware<Provides, E, Requires>` | A plain server middleware function: `(httpEffect, { endpoint, group }) => Effect<HttpServerResponse, ...>`. |
| `HttpApiMiddlewareSecurity<Security, Provides, E, Requires>` | A security middleware: a record keyed by scheme name, each value receiving `{ credential, endpoint, group }`. |
| `HttpApiMiddlewareClient<E, CE, R>` | A client middleware function receiving `{ endpoint, group, request, next }`. |
| `Provides<A>` / `Requires<A>` | Extract the provided / required services from a middleware id. |
| `ApplyServices<A, R>` | `Exclude<R, Provides<A>> \| Requires<A>` — how middleware rewrites a handler's requirements. |
| `Error<A>` / `ErrorSchema<A>` | Extract the decoded error type / error schema of a middleware. |
| `ClientError<A>` / `MiddlewareClient<A>` | Client error type / `ForClient` marker for `requiredForClient: true` middleware. |
| `ForClient<Id>` | The service marker installed by `layerClient`. |

## HttpApiBuilder security helpers

These low-level helpers live in `HttpApiBuilder` and are useful when you decode or
set credentials manually (for example, in cookie-based login/logout flows). When
you attach a security middleware to a group, the builder already calls
`securityDecode` for you and passes the result as `credential` — reach for these
only for bespoke flows.

### securityDecode

Decodes the credential for a given `HttpApiSecurity` scheme from the current
request. Returns the scheme's credential type and requires `HttpServerRequest`
(plus parsed search params for query keys). On a missing/invalid credential it
yields an empty `Redacted("")` (or empty `Credentials` for basic) rather than
failing.

```ts
import { Effect, Redacted } from "effect"
import { HttpApiBuilder, HttpApiSecurity } from "effect/unstable/httpapi"

const decodeKey = Effect.gen(function*() {
  const credential = yield* HttpApiBuilder.securityDecode(
    HttpApiSecurity.apiKey({ key: "session", in: "cookie" })
  )
  return Redacted.value(credential) // the raw cookie value, or "" if absent
})
// => Effect<string, never, HttpServerRequest | ParsedSearchParams>
```

### securitySetCookie

Registers a pre-response handler that writes an API-key cookie onto the outgoing
response — the server side of cookie-based auth. The cookie defaults to `secure`
and `httpOnly`; pass `options` to override (e.g. `sameSite`, `path`, `maxAge`).
Accepts the cookie `value` as a `string` or `Redacted<string>`.

```ts
import { Effect } from "effect"
import { HttpApiBuilder, HttpApiSecurity } from "effect/unstable/httpapi"

const sessionKey = HttpApiSecurity.apiKey({ key: "session", in: "cookie" })

const login = Effect.gen(function*() {
  // ...authenticate the user, mint a session token...
  yield* HttpApiBuilder.securitySetCookie(sessionKey, "session-token-123", {
    sameSite: "strict",
    path: "/"
  })
  // response now carries `Set-Cookie: session=session-token-123; Secure; HttpOnly; ...`
})
// => Effect<void, never, HttpServerRequest>
```
**Tip:** A typical cookie-auth flow pairs the two: a security middleware uses
`apiKey({ in: "cookie" })` to guard endpoints (the builder calls `securityDecode`
for you), the login handler issues the cookie with `securitySetCookie`, and a
logout handler overwrites it with an empty value and a past `maxAge`.