Skip to content

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

Merged
tim-smart merged 4 commits intoEffect-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 intoEffect-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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

@effect/platform HttpApi: middleware is skipped

3 participants