Repository navigation
feat: route a remote's server functions to its own server - #19
Merged
Merged
Conversation
commit: |
Nsttt
force-pushed
the
feat/remote-server-function-routing
branch
from
November 7, 2026 17:41
28f3c60 to
9a7d22b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #18. Review that PR first; this one's diff is against its branch.
Problem
A remote's exposed component that calls its own
createServerFndoesn't work when a host renders it:TSS_SERVER_FN_BASE + idon the page's origin, which is the host's. The host doesn't know the ID, so the call fails with "Server function info not found".This is the problem @mes113 demonstrated in mes113/module-federation-ssr. The approach follows that prototype (per-remote namespace, a host gateway, HTTP during SSR), rebuilt on public TanStack Start APIs.
Change
Remote: set
tanstackStart({ serverFns: { base: "/_federation/<name>/_serverFn" } }). The adapter defines the remote's federation name in its Node container. It fails the build, naming the exact base to use, when the container calls server functions and the base doesn't match.Host: a new
@module-federation/tanstack/server-functionsexport.Browser path:
handleis a plain function the host mounts in its own request middleware. That's public API, so middleware placed before it (such as authentication) runs first. The prototype's server-entry wrapper bypassed it. The gateway:Sec-Fetch-Site, thenOrigin, thenReferer);accept,content-type,x-tsr-serverFn);Origin, so the remote's default CSRF middleware accepts the server-to-server hop;Set-Cookieand hop-by-hop headers;/or%;502when the remote is down.The timeout covers response headers only, so streamed responses aren't cut off.
SSR path: the container's
createSsrRpcwraps Start's publiccreateClientRpcwith a per-callfetch. Serialization is Start's own. The call goes over HTTP to the origin the host registered (the registry lives on aglobalThissymbol, because the container is a separate bundle). The origin lives only in host config and can be read at runtime. Unlike the prototype, the remote never bakes in its own origin, so one remote build works in every environment.Credentials: cookies and authorization reach the remote only when the host's
headers({ request })callback returns them. It receives the incoming request in the browser path, and the host's current request (from Start's shared AsyncLocalStorage) during SSR.Examples: the Rsbuild SSR remote's card gets an "Ask the remote's server" button. The Rsbuild and Vite SSR hosts both add
src/start.tsand call the remote's function from their route loader, so the answer is in the initial HTML.Proof
New end-to-end scenario, run on dev servers and production builds. For both the Rsbuild and Vite hosts:
403.The standalone remote also answers its own button.
test/server-functions.test.mjsuses a real HTTP upstream to check:Origincan't be overridden;cross-site/same-site/foreignOrigin/foreignReferer/none →403);405/404;502;Removing
serverFns.basefrom the remote makes its build fail with the exact base to set.pnpm format:check,pnpm typecheck, andpnpm test(92/92) pass locally. Codex autoreview is unavailable (local auth 401), so the Claude engine reviewed this: scoped-clean at P2.Known limits (documented)
basepatharen't supported.Responsereturned by a handler passes through unchanged. Start's own redirects are serialized in the payload, so they're unaffected.