Skip to content

fix(platform): preserve fiber context in HttpLayerRouter.addHttpApi so API-level middleware is applied#6147

Merged
tim-smart merged 4 commits into
Effect-TS:mainfrom
syhstanley:fix/httplayerrouter-api-middleware-skipped
Apr 22, 2026
Merged

fix(platform): preserve fiber context in HttpLayerRouter.addHttpApi so API-level middleware is applied#6147
tim-smart merged 4 commits into
Effect-TS:mainfrom
syhstanley:fix/httplayerrouter-api-middleware-skipped

Conversation

@syhstanley
Copy link
Copy Markdown
Contributor

Summary

Fixes #6121@effect/platform HttpApi: middleware is skipped when using HttpLayerRouter.addHttpApi with multiple APIs combined via Layer.mergeAll.

Root Cause

In HttpLayerRouter.addHttpApi, each route handler was wrapped with:

handler: Effect.provide(route.handler, context)

Effect.provide calls fiberRefLocally(currentContext, context) which replaces the entire fiber context with the captured build-time context. This silently discards any services injected at request time — most notably the Session service provided by API-level HttpApiMiddleware via Effect.provideServiceEffect.

Fix

Replace Effect.provide with Effect.mapInputContext to merge the build-time platform services into the runtime fiber context instead of replacing it:

handler: Effect.mapInputContext(route.handler, (input) => Context.merge(context, input))

This preserves all request-time services (including Session from API-level middleware) while still ensuring the required platform services (Etag, FileSystem, HttpPlatform, Path, HttpRouter) are available. This is the same pattern already used by HttpApiBuilder.group internally.

Symptoms Fixed

  • Endpoints returning 200 instead of 401 when rejecting middleware was configured (middleware was silently skipped for APIs after the first in Layer.mergeAll)
  • Endpoints returning 500 "Service not found: Session" when accepting middleware was configured (Session injected by middleware was overwritten before the handler ran)

Tests

Adds packages/platform-node/test/HttpApiLayerRouterMiddleware.test.ts with 5 regression tests covering:

  1. ✅ Protected endpoint returns 401 (not 200) with rejecting middleware
  2. ✅ Session-using endpoint returns 401 (not 500) with rejecting middleware
  3. ✅ External API routes remain accessible without authentication
  4. ✅ Protected endpoint returns 200 with accepting middleware
  5. ✅ Session service is injected and accessible in handler with accepting middleware

Test plan

🤖 Generated with Claude Code

When `HttpLayerRouter.addHttpApi` wrapped route handlers using
`Effect.provide(route.handler, context)`, it replaced the entire
fiber context (via `fiberRefLocally`). This silently discarded any
services injected at runtime — most notably `Session` provided by
API-level `HttpApiMiddleware` via `provideServiceEffect`.

The bug manifested as:
- Endpoints returning 200 instead of 401 when rejecting middleware
  was configured (middleware was skipped entirely for the second
  API in `Layer.mergeAll`)
- Endpoints returning 500 "Service not found: Session" when
  accepting middleware was configured (Session injected by middleware
  was overwritten before the handler ran)

Fix: replace `Effect.provide` with `Effect.mapInputContext` so the
build-time platform services context is *merged* into the runtime
fiber context rather than replacing it. This preserves all
request-time services (including Session from API-level middleware).

Adds regression tests for GitHub issue Effect-TS#6121.

Fixes Effect-TS#6121

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@syhstanley syhstanley requested a review from tim-smart as a code owner March 26, 2026 12:02
@github-project-automation github-project-automation Bot moved this to Discussion Ongoing in PR Backlog Mar 26, 2026
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Mar 26, 2026

🦋 Changeset detected

Latest commit: 2f19487

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 31 packages
Name Type
@effect/platform Patch
@effect/cli Patch
@effect/cluster Patch
@effect/experimental Patch
@effect/opentelemetry Patch
@effect/platform-browser Patch
@effect/platform-bun Patch
@effect/platform-node-shared Patch
@effect/platform-node Patch
@effect/rpc Patch
@effect/sql-clickhouse Patch
@effect/sql-d1 Patch
@effect/sql-drizzle Patch
@effect/sql-libsql Patch
@effect/sql-mssql Patch
@effect/sql-mysql2 Patch
@effect/sql-pg Patch
@effect/sql-sqlite-bun Patch
@effect/sql-sqlite-node Patch
@effect/sql Patch
@effect/workflow Patch
@effect/ai Patch
@effect/ai-amazon-bedrock Patch
@effect/ai-anthropic Patch
@effect/ai-google Patch
@effect/ai-openai Patch
@effect/ai-openrouter Patch
@effect/sql-sqlite-do Patch
@effect/sql-sqlite-react-native Patch
@effect/sql-sqlite-wasm Patch
@effect/sql-kysely Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@tim-smart tim-smart enabled auto-merge (squash) April 22, 2026 22:58
@tim-smart tim-smart merged commit 518d0e3 into Effect-TS:main Apr 22, 2026
11 checks passed
@github-project-automation github-project-automation Bot moved this from Discussion Ongoing to Done in PR Backlog Apr 22, 2026
@github-actions github-actions Bot mentioned this pull request Apr 22, 2026
Hoishin added a commit to Hoishin/effect that referenced this pull request May 18, 2026
* upstream/main: (126 commits)
  fix(@effect/cli): use Ansi.blackBright for Weak spans so --help is readable on dark terminals (Effect-TS#6208)
  fix(cli): replace all hyphens in shell completion command names (Effect-TS#6213)
  Version Packages (Effect-TS#6218)
  Support null K8s lastTransitionTime values (Effect-TS#6217)
  Version Packages (Effect-TS#6197)
  Backport workflow suspension failure fixes (Effect-TS#6196)
  clone full v4 history
  Version Packages (Effect-TS#6195)
  preserve fiber context in HttpLayerRouter.addHttpApi so API-level middleware is applied (Effect-TS#6147)
  correct typos in source code (receive, separate) (Effect-TS#6110)
  docs(@effect/vitest): fix return type in fails example (Effect-TS#6163)
  make RpcSerialization.msgPack options configurable (Effect-TS#6161)
  update msgpackr to 1.11.10 for Cloudflare Workers compatibility (Effect-TS#6191)
  fix: floor TestClock nanoseconds before BigInt conversion (Effect-TS#6194)
  chore: disable TypeScript's nightly workflow (Effect-TS#6193)
  Version Packages (Effect-TS#6189)
  (fix: ai-openai) Remove strict param from the OpenAI request body (Effect-TS#6188)
  Version Packages (Effect-TS#6142)
  fix(ai-openai): deduplicate response.output items to prevent invalid JSON concatenation (Effect-TS#6178)
  Change 'in-memory' to 'in_memory' in prompt cache enums (Effect-TS#6187)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

@effect/platform HttpApi: middleware is skipped

3 participants