Skip to content

Conversation

@gzaripov
Copy link

@gzaripov gzaripov commented Oct 31, 2025

Adds .rawHandler() method to createServerFn() that provides direct access to the raw Request object without automatic body parsing. This enables:

  • Streaming large file uploads directly to cloud storage without buffering in memory
  • Enforcing file size limits during upload rather than after
  • Implementing custom body parsing with libraries like busboy
  • Minimizing memory footprint for file operations

The raw handler receives { request, signal, context } where:

  • request: Raw Request object with unread body stream
  • signal: AbortSignal for cancellation support
  • context: Full middleware context (auth, user info, etc.)

Closes #5704

Summary by CodeRabbit

  • New Features

    • Added rawHandler() method for server functions, enabling streaming file uploads and direct access to the raw Request object.
    • Provides granular control over request handling with built-in backpressure and memory efficiency.
  • Documentation

    • Added comprehensive guide covering rawHandler() usage, streaming patterns, and comparison with standard handler() approach.

Adds .rawHandler() method to createServerFn() that provides direct access to the raw Request object without automatic body parsing. This enables:

- Streaming large file uploads directly to cloud storage without buffering in memory
- Enforcing file size limits during upload rather than after
- Implementing custom body parsing with libraries like busboy
- Minimizing memory footprint for file operations

The raw handler receives { request, signal, context } where:
- request: Raw Request object with unread body stream
- signal: AbortSignal for cancellation support
- context: Full middleware context (auth, user info, etc.)

Closes TanStack#5704
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 31, 2025

Walkthrough

This PR adds rawHandler() method support to server functions, enabling direct access to raw Request objects and bypassing automatic FormData parsing. Changes span documentation, client/server middleware wiring, plugin compilation, and request handling logic to facilitate streaming file uploads with size enforcement.

Changes

Cohort / File(s) Change Summary
Documentation
docs/start/framework/react/guide/server-functions.md
Adds comprehensive guide for rawHandler() including rationale, basic usage, streaming examples, client-side integration, and comparison table between .handler() vs .rawHandler() with notes on parsing, memory usage, and requirements
Client-side rawHandler implementation
packages/start-client-core/src/createServerFn.ts
Introduces RawServerFn type, rawHandler() builder method with dual execution paths (client middleware + __executeServer), enhanced serverFnBaseToMiddleware() to detect and branch on options.rawHandler, and modified middleware options to include optional request field
Plugin compiler wiring
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts, handleCreateServerFn.ts, plugin.ts
Expands ServerFn lookup to recognize both "handler" and "rawHandler" as valid entry points; updates handler method resolution logic to validate presence of either; adjusts file scanning include filter and error messaging for both handler types
Server-side raw handler bypass
packages/start-server-core/src/server-functions-handler.ts
Adds bypass path that checks action.__rawHandler flag to skip FormData parsing, directly invoking handler with context and signal parameters
Test coverage
packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
Adds two test cases validating rawHandler and inline rawHandler compilation, verifying client/server-side wiring, __executeServer delegation, and raw function argument passing across boundaries

Sequence Diagram

sequenceDiagram
    participant Client
    participant ClientMiddleware as Client<br/>Middleware
    participant Server
    participant ServerMiddleware as Server<br/>Middleware
    participant RawHandler as Raw<br/>Handler Fn

    alt Normal Handler Path
        Client->>ClientMiddleware: Execute client middleware
        ClientMiddleware->>Server: Send parsed payload
        Server->>Server: Parse FormData
        Server->>ServerMiddleware: Execute server middleware
        ServerMiddleware->>ClientMiddleware: Execute server middleware
        ClientMiddleware->>Server: Call handler(ctx)
    else Raw Handler Path (New)
        Client->>ClientMiddleware: Execute client middleware
        ClientMiddleware->>Server: Send raw request + signal
        Server->>ServerMiddleware: Execute server middleware
        ServerMiddleware->>RawHandler: Call rawHandler({request, signal, context})
        RawHandler->>RawHandler: Stream/process raw body
        RawHandler->>Server: Return Response
    end
    Server-->>Client: Return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring extra attention:
    • Middleware branching logic in createServerFn.ts—verify that options.rawHandler flag correctly routes execution between standard and raw paths
    • Dual-method approach validation—ensure __executeServer properly delegates server-side logic
    • Server-side bypass path in server-functions-handler.ts—confirm interaction with existing FormData logic doesn't break standard handlers
    • Test coverage—verify both inline and imported rawHandler scenarios are properly validated across client/server boundaries

Possibly related PRs

Poem

🐰 A raw stream flows so pure and true,
No buffering, no middle ground in view,
Files dance direct from user to cloud,
Swift uploads whisper, unencumbered and proud!
The rabbit hops faster with streaming so light! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat(server-functions): add rawHandler for streaming file uploads" is concise, clear, and directly summarizes the main change: introducing a rawHandler method to createServerFn that enables streaming file uploads. The title uses standard commit conventions and meaningfully communicates the primary feature being added across all modified files, from documentation and client-side APIs to server-side implementations and compiler support. This accurately reflects the core objective described in the PR objectives and linked issue.
Linked Issues Check ✅ Passed The PR successfully implements the requirements from linked issue #5704 across multiple areas: the server-side handler now bypasses automatic formData() parsing via a dedicated rawHandler path, allowing direct access to the raw request body stream; the client-side implementation provides the rawHandler method with signature { request, signal, context } enabling developers to handle streaming uploads without buffering; the compiler and plugin support recognizes and wires rawHandler correctly; and comprehensive documentation demonstrates streaming examples with size enforcement and backpressure handling. The implementation satisfies all core objectives: preventing automatic FormData parsing, enabling true streaming uploads, allowing per-upload file size limits, supporting backpressure with minimal memory footprint, providing a dedicated handler type, and avoiding the need for separate routes.
Out of Scope Changes Check ✅ Passed All code changes in the PR are directly related to implementing the rawHandler feature for streaming file uploads. The modifications span documentation, client-side API additions, compiler enhancements, server-side request handling, and test coverage—all components necessary to fully implement the rawHandler functionality specified in issue #5704. No unrelated refactorings, cleanup tasks, or tangential modifications are present in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docs/start/framework/react/guide/server-functions.md (1)

330-330: Minor grammatical issue in sentence.

The sentence "Return Response objects binary data, or custom content types." is missing a word.

Apply this diff to fix the grammar:

-Return `Response` objects binary data, or custom content types.
+Return `Response` objects for binary data, or custom content types.
🧹 Nitpick comments (1)
packages/start-client-core/src/createServerFn.ts (1)

806-814: Fail fast when the Request is missing

If getStartContextServerOnly() ever fails to hydrate a Request (for example, somebody reuses the raw handler from a background job without a live HTTP request), we will hand undefined to userland and the very first request.* access will explode with a cryptic “cannot read properties of undefined”. A defensive guard here gives a clear error and prevents downstream middleware from running in an invalid state.

           const ctxWithRequest = ctx as typeof ctx & { request: Request }
+          if (!ctxWithRequest.request) {
+            throw new Error(
+              'createServerFn.rawHandler() requires a Request on the server context.',
+            )
+          }
           result = await (options.serverFn as any)?.({
             request: ctxWithRequest.request,
             signal: ctx.signal,
             context: ctx.context,
           })
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 943d616 and daa022e.

📒 Files selected for processing (7)
  • docs/start/framework/react/guide/server-functions.md (1 hunks)
  • packages/start-client-core/src/createServerFn.ts (6 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (1 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts (4 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1 hunks)
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts (1 hunks)
  • packages/start-server-core/src/server-functions-handler.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/server-functions.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/server-functions.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-client-core/src/createServerFn.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts
  • packages/start-server-core/src/server-functions-handler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-client-core/src/createServerFn.ts
🧬 Code graph analysis (2)
packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts (1)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (1)
  • compile (203-268)
packages/start-client-core/src/createServerFn.ts (3)
packages/start-client-core/src/index.tsx (5)
  • CompiledFetcherFn (60-60)
  • Register (100-100)
  • CompiledFetcherFnOptions (59-59)
  • executeMiddleware (80-80)
  • Fetcher (61-61)
packages/start-client-core/src/getStartContextServerOnly.ts (1)
  • getStartContextServerOnly (4-4)
packages/start-client-core/src/createMiddleware.ts (1)
  • AssignAllServerFnContext (325-343)
🔇 Additional comments (7)
packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts (2)

98-145: LGTM! Well-structured test for rawHandler with identifier function.

The test correctly verifies that:

  • Client compilation creates a wrapper delegating to __executeServer
  • Server compilation preserves the original myRawFunc and passes it as the second argument
  • POST method is used appropriately for the rawHandler use case

The test follows the established pattern and provides good coverage for the rawHandler feature.


147-196: LGTM! Comprehensive test for inline rawHandler.

The test properly validates the inline function pattern for rawHandler, ensuring that:

  • The inline async function is correctly compiled on both client and server
  • The function signature { request, signal } is preserved
  • Server compilation passes the inline function as the second argument to rawHandler

This complements the identifier-based test and provides complete coverage for both usage patterns.

docs/start/framework/react/guide/server-functions.md (5)

130-142: LGTM! Clear introduction to rawHandler feature.

The section introduction and use cases effectively communicate:

  • When to use .rawHandler() over .handler()
  • Key benefits: streaming, size enforcement, backpressure, memory efficiency
  • Alignment with PR objectives for streaming file uploads

144-163: LGTM! Clear basic example.

The basic raw handler example effectively demonstrates:

  • Access to the raw request object and signal
  • Reading request headers and body
  • Returning a Response object

The example provides a good foundation before introducing more complex streaming scenarios.


165-198: LGTM! Comprehensive streaming example.

The streaming file upload example effectively demonstrates:

  • Middleware integration and context access (authentication, user info)
  • Direct access to request.body stream
  • Size limit enforcement during upload
  • Cancellation support via signal

The streamToStorage function serves as a clear placeholder demonstrating the streaming concept without cluttering the example with implementation details.


200-239: LGTM! Clear client usage and comparison.

The client-side example and comparison table effectively:

  • Demonstrate practical FormData usage with rawHandler
  • Highlight key differences in body parsing, memory usage, and size limit enforcement
  • Correctly document parameter signatures for both handler types
  • Provide clear guidance on when to use each approach

240-246: LGTM! Important notes clearly document rawHandler behavior.

The notes effectively communicate critical differences:

  • No automatic parsing (manual handling required)
  • Parameter structure: { request, context, signal } vs { data, context, signal }
  • Middleware context availability (authentication, user info)
  • Response return requirement

These notes help prevent common mistakes and clarify expectations.

@schiller-manuel
Copy link
Contributor

would rather prefer the flag in createServerFn, however we first need to find out how to handle those raw request handlers when it comes to (global) server function middlewares

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support streaming file uploads in server functions

2 participants