Skip to content

HttpRouter.toWebHandler rejects valid HttpApiBuilder layers at the type level (platform services in R) #6768

Description

@craigsmitham

Summary

HttpRouter.toWebHandler's layer parameter is constrained to
Layer.Layer<A, E, R> with R extends HttpRouter | Request<"Requires", any> | Request<"GlobalRequires", any> | Request<"Error", any> | Request<"GlobalError", any>.

Layers produced by HttpApiBuilder.layer(api) (with handler groups provided) also carry the platform services that toWebHandler itself provides at runtime — FileSystem, HttpPlatform, Path, Generator — in their input channel. Those members are not assignable to the R constraint, so a handler layer that is perfectly valid at runtime is rejected at the type level, and every HttpApi-on-web-handler composition needs a requirements-channel cast.

Reproduction (effect@4.0.0-beta.101)

import { HttpRouter } from "effect/unstable/http"
import { HttpApi, HttpApiBuilder, HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi"
import * as Layer from "effect/Layer"
import * as Effect from "effect/Effect"
import * as Schema from "effect/Schema"

const Api = HttpApi.make("api").add(
  HttpApiGroup.make("g").add(
    HttpApiEndpoint.get("hello", "/", { success: Schema.String })
  )
)

const Handlers = HttpApiBuilder.group(Api, "g", (handlers) =>
  handlers.handleAll({ hello: () => Effect.succeed("hi") })
)

const layer = HttpApiBuilder.layer(Api).pipe(Layer.provide(Handlers))

// Type error: the layer's input channel includes FileSystem | HttpPlatform |
// Path | Generator (plus HttpRouter/Request phantoms), which the R constraint
// rejects — even though toWebHandler provides those services at runtime.
const { handler } = HttpRouter.toWebHandler(layer)

Error shape (abbreviated):

Argument of type 'Layer<..., ..., HttpRouter | Request<"GlobalError", unknown> |
  Exclude<... FileSystem | Generator | HttpPlatform | Path ...>>' is not
assignable to parameter of type 'Layer<..., ..., never>'

Ask

Widen toWebHandler's R constraint (or Exclude the provided platform services from the input before constraining) so HttpApi handler layers typecheck without a cast. Currently the practical workaround is:

HttpRouter.toWebHandler(layer as Layer.Layer<unknown, E, never>)

centralized behind one sanctioned helper — it would be great to delete it.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions