Skip to content

Conversation

Copy link

Copilot AI commented Nov 5, 2025

Adds symmetric toWeb conversion to complement the existing fromWeb, enabling bidirectional conversion between HttpServerRequest and global Request.

Changes

Public API (HttpServerRequest.ts)

  • Export toWeb: (self: HttpServerRequest) => Request

Implementation (internal/httpServerRequest.ts)

  • Return original Request when source instanceof globalThis.Request (preserves identity and body streams)
  • Reconstruct new Request for non-wrapped implementations using toURL(), method, and headers
  • Body intentionally omitted for non-wrapped requests to maintain synchronous signature

Tests (HttpServerRequest.toWeb.test.ts)

  • Round-trip identity: toWeb(fromWeb(request)) === request
  • URL reconstruction with protocol detection via x-forwarded-proto
  • Header preservation and host fallback to localhost

Example

// Round-trip preserves original Request
const original = new Request("https://api.example.com/users", {
  method: "POST",
  headers: { "Authorization": "Bearer token" },
  body: JSON.stringify({ name: "test" })
})

const httpReq = HttpServerRequest.fromWeb(original)
const restored = HttpServerRequest.toWeb(httpReq)

console.log(restored === original) // true - identity preserved

Limitation

Non-wrapped HttpServerRequest implementations lose body content on conversion (synchronous API constraint). Wrapped requests from fromWeb() preserve all properties including streaming body.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • google.com
    • Triggering command: node (vitest 3) (dns block)
  • jsonplaceholder.typicode.com
    • Triggering command: node (vitest 3) (dns block)
    • Triggering command: node (vitest 1) (dns block)
  • pkg.pr.new
    • Triggering command: node /usr/local/bin/pnpm install (dns block)
    • Triggering command: node /usr/local/bin/pnpm install --no-frozen-lockfile --ignore-scripts (dns block)
    • Triggering command: node /usr/local/bin/pnpm install --filter @effect/platform --ignore-scripts (dns block)
  • www.google.com
    • Triggering command: node (vitest 3) (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Add a toWeb conversion for HttpServerRequest and tests.

Background

  • The public API currently exposes fromWeb: (request: Request) => HttpServerRequest implemented in packages/platform/src/internal/httpServerRequest.ts as fromWeb(request) -> ServerRequestImpl(request, removeHost(request.url)). There is no symmetric conversion back to a global Request.
  • The repository already has utilities to reconstruct a URL from a ServerRequest (toURL) and the ServerRequestImpl stores the original Request as source.

Goals

  1. Export a new conversion in the public API: packages/platform/src/HttpServerRequest.ts:

    • Add: export const toWeb: (self: HttpServerRequest) => Request = internal.toWeb
      This mirrors the existing fromWeb export and keeps the public API symmetric.
  2. Implement internal.toWeb in packages/platform/src/internal/httpServerRequest.ts:

    • Export a new function toWeb with signature (self: ServerRequest.HttpServerRequest) => globalThis.Request.
    • If the provided HttpServerRequest is a wrapper around a global Request (i.e. it has a .source that is an instance of global Request), return that original Request to preserve identity and the body/streaming behavior.
    • Otherwise, reconstruct a new Request using the best available information:
      • URL: try to use the existing toURL(self) helper (it returns Option). If it yields a URL, use url.href; otherwise fall back to self.originalUrl or '/'.
      • Method: self.method
      • Headers: copy headers from self.headers. The Headers type in platform is a plain record used like self.headers["content-type"], so passing it as RequestInit.headers is appropriate (cast to any where needed).
      • Body: do not attempt to synchronously convert Effect-wrapped/streaming bodies; omit the body for non-wrapping implementations. Documented limitation: only the original wrapped Request preserves its body. This keeps the API synchronous and ergonomic.
    • Ensure the function uses runtime checks against globalThis.Request to avoid breaking environments without a global Request.
  3. Add unit tests: packages/platform/test/HttpServerRequest.toWeb.test.ts

    • Test A: round-trip identity for wrapped requests
      • Create a global Request with url/method/body/headers
      • Call fromWeb(req) to obtain HttpServerRequest
      • Call toWeb(httpReq) and assert the returned object is strictly equal (===) to the original Request.
    • Test B: reconstructs URL/method/headers for non-wrapped implementation
      • Create a minimal stub object implementing HttpServerRequest that does not wrap a source Request but provides method, headers, url/originalUrl and necessary properties used by toWeb/toURL (headers.host when necessary).
      • Call toWeb(stub) and assert the returned Request has the expected url (absolute or path depending on toURL), method, and headers values present.
    • Keep tests simple and synchronous (no streaming conversions). Use the same test runner and style as other platform tests in the repository.

Notes and rationale

  • Choosing a synchronous toWeb that returns a Request keeps the API symmetric and ergonomic: callers who need the original Request (common case when the request originated from a Web API) get it back; callers who construct custom HttpServerRequest instances get a best-effort reconstructed Request without body.
  • Attempting to preserve streaming bodies for arbitrary HttpServerRequest implementations would require async/effectful work and would change the exported type to return an Effect/Promise. That would be surprising in a small utility and reduce ergonomics for the common case where the wrapper already has the original Request.

Files to change

  • Modify: packages/platform/src/HttpServerRequest.ts (add export)
  • Modify: packages/platform/src/internal/httpServerRequest.ts (add implementation for internal.toWeb)
  • Add: packages/platform/test/HttpServerRequest.toWeb.test.ts (unit tests described above)

Please open a PR that implements these changes, includes tests, and a brief PR body describing the behavior and limitation around request body preservation for non-wrapping requests.

This pull request was created as a result of the following prompt from Copilot chat.

Add a toWeb conversion for HttpServerRequest and tests.

Background

  • The public API currently exposes fromWeb: (request: Request) => HttpServerRequest implemented in packages/platform/src/internal/httpServerRequest.ts as fromWeb(request) -> ServerRequestImpl(request, removeHost(request.url)). There is no symmetric conversion back to a global Request.
  • The repository already has utilities to reconstruct a URL from a ServerRequest (toURL) and the ServerRequestImpl stores the original Request as source.

Goals

  1. Export a new conversion in the public API: packages/platform/src/HttpServerRequest.ts:

    • Add: export const toWeb: (self: HttpServerRequest) => Request = internal.toWeb
      This mirrors the existing fromWeb export and keeps the public API symmetric.
  2. Implement internal.toWeb in packages/platform/src/internal/httpServerRequest.ts:

    • Export a new function toWeb with signature (self: ServerRequest.HttpServerRequest) => globalThis.Request.
    • If the provided HttpServerRequest is a wrapper around a global Request (i.e. it has a .source that is an instance of global Request), return that original Request to preserve identity and the body/streaming behavior.
    • Otherwise, reconstruct a new Request using the best available information:
      • URL: try to use the existing toURL(self) helper (it returns Option). If it yields a URL, use url.href; otherwise fall back to self.originalUrl or '/'.
      • Method: self.method
      • Headers: copy headers from self.headers. The Headers type in platform is a plain record used like self.headers["content-type"], so passing it as RequestInit.headers is appropriate (cast to any where needed).
      • Body: do not attempt to synchronously convert Effect-wrapped/streaming bodies; omit the body for non-wrapping implementations. Documented limitation: only the original wrapped Request preserves its body. This keeps the API synchronous and ergonomic.
    • Ensure the function uses runtime checks against globalThis.Request to avoid breaking environments without a global Request.
  3. Add unit tests: packages/platform/test/HttpServerRequest.toWeb.test.ts

    • Test A: round-trip identity for wrapped requests
      • Create a global Request with url/method/body/headers
      • Call fromWeb(req) to obtain HttpServerRequest
      • Call toWeb(httpReq) and assert the returned object is strictly equal (===) to the original Request.
    • Test B: reconstructs URL/method/headers for non-wrapped implementation
      • Create a minimal stub object implementing HttpServerRequest that does not wrap a source Request but provides method, headers, url/originalUrl and necessary properties used by toWeb/toURL (headers.host when necessary).
      • Call toWeb(stub) and assert the returned Request has the expected url (absolute or path depending on toURL), method, and headers values present.
    • Keep tests simple and synchronous (no streaming conversions). Use the same test runner and style as other platform tests in the repository.

Notes and rationale

  • Choosing a synchronous toWeb that returns a Request keeps the API symmetric and ergonomic: callers who need the original Request (common case when the request originated from a Web API) get it back; callers who construct custom HttpServerRequest instances get a best-effort reconstructed Request without body.
  • Attempting to preserve streaming bodies for arbitrary HttpServerRequest implementations would require async/effectful work and would change the exported type to return an Effect/Promise. That would be surprising in a small utility and reduce ergonomics for the common case where the wrapper already has the original Request.

Files to change

  • Modify: packages/platform/src/HttpServerRequest.ts (add export)
  • Modify: packages/platform/src/internal/httpServerRequest.ts (add implementation for internal.toWeb)
  • Add: packages/platform/test/HttpServerRequest.toWeb.test.ts (unit tests described above)

Please open a PR that implements these changes, includes tests, and a brief PR body describing the behavior and limitation around request body preservation for non-wrapping requests.


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@changeset-bot
Copy link

changeset-bot bot commented Nov 5, 2025

⚠️ No Changeset found

Latest commit: 967984f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

Copilot AI changed the title [WIP] Add toWeb conversion for HttpServerRequest and tests Add toWeb conversion for HttpServerRequest Nov 5, 2025
Copilot finished work on behalf of kitlangton November 5, 2025 23:59
Copilot AI requested a review from kitlangton November 5, 2025 23:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

2 participants