Skip to content

feat: allow region override based on request#1187

Merged
juliusmarminge merged 8 commits into
mainfrom
multiregions
May 8, 2025
Merged

feat: allow region override based on request#1187
juliusmarminge merged 8 commits into
mainfrom
multiregions

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented May 7, 2025

Copy link
Copy Markdown
Member

experimental

Summary by CodeRabbit

  • New Features
    • Introduced experimental support for dynamic upload region selection based on user location (private beta; requires backend feature flag).
    • Added ability for middleware to specify a preferred upload region, enabling region-aware uploads.
  • Documentation
    • Added a new section detailing dynamic region selection, including usage examples and implementation guidance.
  • Style
    • Updated exports to clearly mark experimental region selection features across supported frameworks.

@changeset-bot

changeset-bot Bot commented May 7, 2025

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: dba8678

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

@vercel

vercel Bot commented May 7, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
docs-uploadthing ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 8, 2025 0:45am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
legacy-docs-uploadthing ⬜️ Ignored (Inspect) Visit Preview May 8, 2025 0:45am

@coderabbitai

coderabbitai Bot commented May 7, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This change introduces dynamic region selection for file uploads, allowing the upload region to be chosen based on user location. It adds a new documentation section, updates middleware and internal types to support region metadata, and adjusts internal logic to propagate and utilize a preferred region throughout the upload process. Several exports are updated to expose the experimental region selection feature.

Changes

File(s) Change Summary
docs/src/app/(docs)/concepts/regions-acl/page.mdx Added a new documentation section describing dynamic region selection (private beta), with an implementation example using middleware to set upload region metadata based on user continent.
packages/uploadthing/src/_internal/config.ts Modified IngestUrl to accept an optional preferredRegion argument, updating logic to select the specified region if available, or default to the first region.
packages/uploadthing/src/_internal/handler.ts Propagated region preference from middleware metadata through the upload handling flow; runRouteMiddleware now returns preferredRegion, which is passed to IngestUrl. Callback request handling now uses a dynamic origin from the request body for callback posting.
packages/uploadthing/src/_internal/types.ts Added UTRegionAlias type, UTRegion symbol, and extended ValidMiddlewareObject to include an optional region marker property keyed by [UTRegion].
packages/uploadthing/src/effect-platform.ts
packages/uploadthing/src/express.ts
packages/uploadthing/src/fastify.ts
packages/uploadthing/src/h3.ts
packages/uploadthing/src/next-legacy.ts
packages/uploadthing/src/next.ts
packages/uploadthing/src/remix.ts
packages/uploadthing/src/server.ts
Updated export statements to include UTRegion as experimental_UTRegion with comments indicating it is an experimental feature requiring backend feature flagging. No other changes.
packages/uploadthing/src/sdk/utils.ts Updated call to IngestUrl in generatePresignedUrl to explicitly pass undefined as the argument.
playground/app/api/uploadthing/route.ts Updated middleware in uploadRouter.anyPrivate to dynamically select upload region based on the x-vercel-ip-continent header, returning the region via the experimental_UTRegion key. Changed ACL for blob file type from "private" to "public-read".

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Middleware
    participant Handler
    participant IngestUrl
    participant StorageRegion

    User->>Middleware: Initiate upload request (with headers)
    Middleware->>Middleware: Extract continent from headers
    Middleware->>Middleware: Map continent to region
    Middleware->>Handler: Pass preferredRegion in metadata
    Handler->>IngestUrl: Request URL with preferredRegion
    IngestUrl->>StorageRegion: Select region based on preferredRegion
    StorageRegion-->>IngestUrl: Return region URL
    IngestUrl-->>Handler: Return ingest URL
    Handler-->>User: Provide upload URL
Loading

Suggested labels

examples

Suggested reviewers

  • markflorkowski

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac8360 and dba8678.

📒 Files selected for processing (2)
  • packages/uploadthing/test/node/config.test.ts (2 hunks)
  • packages/uploadthing/test/node/request-handler.test.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: typecheck
  • GitHub Check: lint
  • GitHub Check: analyze-bundle (current-pr)
  • GitHub Check: e2e-node (backend-adapters)
  • GitHub Check: e2e-node (minimal-pagedir)
  • GitHub Check: build
  • GitHub Check: e2e-node (minimal-appdir)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (3)
packages/uploadthing/test/node/request-handler.test.ts (1)

410-414: LGTM! The test now includes the origin for region-aware upload handling.

The added origin property in the payload correctly tests the new functionality that allows dynamic region selection based on the request origin. This change aligns with updates to the callback request body schema in the handler.

packages/uploadthing/test/node/config.test.ts (2)

251-251: The change correctly adapts to the updated function signature.

The modification properly calls IngestUrl with undefined to adapt to the new function signature that accepts an optional preferredRegion parameter.


266-266: The change correctly adapts to the updated function signature.

The modification properly calls IngestUrl with undefined to adapt to the new function signature that accepts an optional preferredRegion parameter.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@juliusmarminge juliusmarminge added the ❗ infra changes required Infra changes required for this change to work label May 7, 2025
@pkg-pr-new

pkg-pr-new Bot commented May 7, 2025

Copy link
Copy Markdown
More templates

npm i https://pkg.pr.new/pingdotgg/uploadthing/@uploadthing/react@1187
npm i https://pkg.pr.new/pingdotgg/uploadthing@1187

commit: dba8678

@juliusmarminge juliusmarminge added the ✅ infra changes implemented The necessary infra changes have been shipped label May 8, 2025
@juliusmarminge juliusmarminge marked this pull request as ready for review May 8, 2025 11:57
@github-actions github-actions Bot added the sdk label May 8, 2025

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (2)
packages/uploadthing/src/_internal/handler.ts (2)

328-335: 🛠️ Refactor suggestion

origin field should be validated / normalised before use

origin arrives from the signed payload, which gives integrity but not semantic correctness:

  • an empty string or something that is not a valid absolute URL will throw later when prependUrl concatenates it
  • an unexpected protocol (e.g. http, ftp) could bypass TLS expectations
  • a value like https://evil.com@ingest.uploadthing.com is syntactically valid and passes a naïve “startsWith” test

Consider defensive parsing right after decoding:

+    // Validate that `origin` is a well-formed https URL before we rely on it
+    let safeOrigin: string | null = null;
+    try {
+      const url = new URL(requestInput.origin);
+      if (url.protocol !== "https:") {
+        throw new Error("origin must use https");
+      }
+      safeOrigin = url.origin; // normalised form (drops path / query)
+    } catch (err) {
+      yield* Effect.logError("Invalid origin in callback")
+        .pipe(Effect.annotateLogs("origin", requestInput.origin));
+      return yield* new UploadThingError({
+        code: "BAD_REQUEST",
+        message: "Invalid origin",
+      });
+    }

…and then pass safeOrigin downstream.
[security]


387-395: 🛠️ Refactor suggestion

Use the validated origin when building the callback URL

Following the previous comment, the call site should reference the normalised/validated value instead of requestInput.origin directly:

-        HttpClientRequest.prependUrl(requestInput.origin),
+        HttpClientRequest.prependUrl(safeOrigin),

This prevents a malicious value from leaking into an outbound request if the validation ever returns early.
[security]

🧹 Nitpick comments (8)
packages/uploadthing/src/fastify.ts (1)

12-19: Consistent experimental export of UTRegion
The experimental_UTRegion symbol is correctly re-exported with a clear JSDoc warning, aligning with other platform adapters and enabling the region override feature.

Consider also exporting the UTRegionAlias type to provide developers with compile-time validation of region codes. For example:

 export {
   UTFiles,
   /**
    * This is an experimental feature.
    * You need to be feature flagged on our backend to use this
    */
   UTRegion as experimental_UTRegion,
 } from "./_internal/types";
+export type { UTRegionAlias } from "./_internal/types";

This addition will make the set of valid region string literals discoverable in IDEs.

packages/uploadthing/src/effect-platform.ts (1)

13-20: Add experimental UTRegion export
Exporting UTRegion as experimental_UTRegion with a JSDoc notice is correct and consistent with other adapters.

Optionally export the UTRegionAlias type to help users leverage TypeScript’s type safety:

 export {
   UTFiles,
   /**
    * This is an experimental feature.
    * You need to be feature flagged on our backend to use this
    */
   UTRegion as experimental_UTRegion,
 } from "./_internal/types";
+export type { UTRegionAlias } from "./_internal/types";

This will make region code types discoverable and assist with IDE autocompletion.

packages/uploadthing/src/express.ts (1)

17-24: Expose experimental region key for Express adapter
You've correctly re-exported UTRegion as experimental_UTRegion with appropriate JSDoc, aligning with the new dynamic region feature.

To aid TypeScript users, consider also exporting the associated region code type:

 export {
   UTFiles,
   /**
    * This is an experimental feature.
    * You need to be feature flagged on our backend to use this
    */
   UTRegion as experimental_UTRegion,
 } from "./_internal/types";
+export type { UTRegionAlias } from "./_internal/types";

This provides compile-time validation of allowed region strings.

packages/uploadthing/src/remix.ts (1)

12-19: Add experimental UTRegion export in Remix adapter
The experimental_UTRegion export is correctly added with a JSDoc warning, consistent with other adapters for enabling dynamic region override.

Optionally include the UTRegionAlias type in exports to expose valid region string literals:

 export {
   UTFiles,
   /**
    * This is an experimental feature.
    * You need to be feature flagged on our backend to use this
    */
   UTRegion as experimental_UTRegion,
 } from "./_internal/types";
+export type { UTRegionAlias } from "./_internal/types";

This will improve developer ergonomics when specifying regions.

packages/uploadthing/src/h3.ts (1)

12-19: Experimental region support for H3 adapter
Correctly re-exported UTRegion as experimental_UTRegion with JSDoc, maintaining consistency and enabling the region override feature.

Consider also exporting the region code type UTRegionAlias for better type discovery and validation:

 export {
   UTFiles,
   /**
    * This is an experimental feature.
    * You need to be feature flagged on our backend to use this
    */
   UTRegion as experimental_UTRegion,
 } from "./_internal/types";
+export type { UTRegionAlias } from "./_internal/types";

This ensures developers have access to the allowed region codes in their code editor.

playground/app/api/uploadthing/route.ts (1)

30-45: Consider improving the fallback logic for region selection

The current implementation is good but has a subtle issue with the fallback logic when a header is missing. When the x-vercel-ip-continent header is missing, it relies on uppercasing undefined and then looks up "US" which isn't directly in the mapping. This works because it falls back to "sea1", but it's not clear from the code.

-      const region = (
-        {
-          AF: "fra1",
-          AN: "sea1",
-          AS: "bom1",
-          EU: "fra1",
-          NA: "sea1",
-          OC: "sea1",
-          SA: "sea1",
-        } as const
-      )[opts.req.headers.get("x-vercel-ip-continent")?.toUpperCase() ?? "US"]!;
+      const continentMap = {
+        AF: "fra1",
+        AN: "sea1",
+        AS: "bom1",
+        EU: "fra1",
+        NA: "sea1",
+        OC: "sea1",
+        SA: "sea1",
+      } as const;
+
+      const continent = opts.req.headers.get("x-vercel-ip-continent")?.toUpperCase();
+      const region = continent ? (continentMap[continent] ?? "sea1") : "sea1";
packages/uploadthing/src/_internal/handler.ts (2)

469-474: preferredRegion should be type-checked and whitelisted

metadata[UTRegion] is currently typed as unknown (symbol index signature).
Passing it unchecked to IngestUrl relies on that function falling back safely, but adding an explicit guard here improves debuggability and prevents weird states:

-      preferredRegion: metadata[UTRegion],
+      preferredRegion:
+        typeof metadata[UTRegion] === "string" && metadata[UTRegion].length > 0
+          ? metadata[UTRegion]
+          : undefined,

Optionally, verify it exists in UTToken.regions to fail fast instead of silently falling back.


507-512: Destructuring is fine – consider naming consistency

The variable is called preferredRegion, while the exported public API symbol is experimental_UTRegion elsewhere. Aligning the internal naming (preferredRegion) with the external (utRegion, experimentalRegion …) could save a quick mental mapping for newcomers.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b4dfb38 and 4ac8360.

⛔ Files ignored due to path filters (1)
  • docs/src/app/(docs)/concepts/regions-acl/multiregion.png is excluded by !**/*.png
📒 Files selected for processing (14)
  • docs/src/app/(docs)/concepts/regions-acl/page.mdx (1 hunks)
  • packages/uploadthing/src/_internal/config.ts (1 hunks)
  • packages/uploadthing/src/_internal/handler.ts (6 hunks)
  • packages/uploadthing/src/_internal/types.ts (2 hunks)
  • packages/uploadthing/src/effect-platform.ts (1 hunks)
  • packages/uploadthing/src/express.ts (1 hunks)
  • packages/uploadthing/src/fastify.ts (1 hunks)
  • packages/uploadthing/src/h3.ts (1 hunks)
  • packages/uploadthing/src/next-legacy.ts (1 hunks)
  • packages/uploadthing/src/next.ts (1 hunks)
  • packages/uploadthing/src/remix.ts (1 hunks)
  • packages/uploadthing/src/sdk/utils.ts (1 hunks)
  • packages/uploadthing/src/server.ts (1 hunks)
  • playground/app/api/uploadthing/route.ts (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (2)
packages/uploadthing/src/sdk/utils.ts (1)
packages/uploadthing/src/_internal/config.ts (1)
  • IngestUrl (85-99)
packages/uploadthing/src/_internal/handler.ts (2)
packages/uploadthing/src/_internal/types.ts (1)
  • UTRegion (37-37)
packages/uploadthing/src/_internal/config.ts (1)
  • IngestUrl (85-99)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: analyze-bundle (current-pr)
  • GitHub Check: lint
  • GitHub Check: e2e-node (backend-adapters)
  • GitHub Check: build
  • GitHub Check: e2e-node (minimal-pagedir)
  • GitHub Check: e2e-node (minimal-appdir)
  • GitHub Check: build
  • GitHub Check: build
🔇 Additional comments (14)
packages/uploadthing/src/sdk/utils.ts (1)

87-87: The explicit undefined parameter adapts to the updated IngestUrl signature.

This change passes undefined as the preferredRegion parameter to the updated IngestUrl function, maintaining backward compatibility while supporting the new region override feature.

packages/uploadthing/src/server.ts (1)

12-19: Good documentation for the experimental feature export.

The export of UTRegion as experimental_UTRegion is properly marked with JSDoc comments indicating its experimental status and backend feature flag requirement.

packages/uploadthing/src/next.ts (1)

12-19: Consistent export pattern for the experimental region feature.

The export implementation matches the pattern used in server.ts, properly exposing the feature for Next.js App Router applications with clear documentation about its experimental status.

packages/uploadthing/src/next-legacy.ts (1)

12-19: Feature consistently exported for legacy Next.js applications.

The experimental region feature is properly exposed for Next.js Pages Router applications, maintaining API consistency across all supported Next.js versions.

packages/uploadthing/src/_internal/config.ts (1)

85-93: LGTM! Well-implemented dynamic region selection

The implementation correctly handles the new region selection logic, attempting to use the provided preferredRegion if it exists in the available regions, with a proper fallback to the first region when needed.

playground/app/api/uploadthing/route.ts (2)

7-7: LGTM! Correct import of the experimental feature

The import of experimental_UTRegion ensures access to the new region selection functionality.


19-19: Verify intent of ACL change from private to public-read

The ACL for blob files was changed from what was likely "private" to "public-read", which is unrelated to the region selection feature. Make sure this change is intentional.

packages/uploadthing/src/_internal/types.ts (3)

22-33: LGTM! Comprehensive region alias type

The UTRegionAlias type properly defines all the supported regions, matching those documented in the regions table.


34-38: LGTM! Well-documented region symbol

The UTRegion symbol is properly defined with clear documentation explaining its purpose for dynamic region selection.


60-60: LGTM! Correct type extension for middleware

The ValidMiddlewareObject interface is correctly extended to include the optional region property.

docs/src/app/(docs)/concepts/regions-acl/page.mdx (2)

50-103: LGTM! Well-documented experimental feature

The documentation clearly explains the dynamic region selection feature, including:

  1. Its private beta status and how to request access
  2. The purpose and benefits of the feature
  3. A practical example that demonstrates implementation
  4. Clear code comments explaining the region selection logic

The example provided correctly demonstrates how to implement the feature in middleware, with proper error handling and fallback logic.


63-63:

Details

✅ Verification successful

Verify image path and accessibility

The documentation references an image ./multiregion.png which appears to be a relative path. Make sure this image exists and is accessible when the documentation is published.


🏁 Script executed:

#!/bin/bash
# Check if the multiregion.png image exists
cd docs/src/app/\(docs\)/concepts/regions-acl/
if [ -f "multiregion.png" ]; then
  echo "Image exists"
else
  echo "Image not found at expected path"
  # Look for it elsewhere
  cd ../../../../../
  fd -t f "multiregion.png"
fi

Length of output: 112


Image path and accessibility verified

The image multiregion.png exists in docs/src/app/(docs)/concepts/regions-acl/ and includes the alt text “Dynamic region selection,” ensuring it will be displayed and accessible when published. No further action needed.

packages/uploadthing/src/_internal/handler.ts (2)

49-49: Import looks correct – keep the unused-import linter in mind

UTRegion is now required downstream, so the addition is correct. If the linter is configured to flag unused imports, double-check that every build target touches code paths that reference UTRegion; otherwise tree–shaking could strip it and the linter may complain in the future.


571-571: Good integration of preferredRegion with IngestUrl

IngestUrl already validates the provided region against the server-supplied list and falls back gracefully—nice touch. No further action required here.

@github-actions

github-actions Bot commented May 8, 2025

Copy link
Copy Markdown
Contributor

📦 Bundle size comparison

Bundle Size (gzip) Visualization
Main 30.55KB See Treemap 📊
PR (93f59a4) 30.55KB See Treemap 📊
Diff No change

@juliusmarminge juliusmarminge added the release canary Trigger a canary release to npm label May 8, 2025
@github-actions

github-actions Bot commented May 8, 2025

Copy link
Copy Markdown
Contributor

A new canary is available for testing. You can install this latest build in your project with:

pnpm add @uploadthing/expo@7.2.3-canary.07fd596
pnpm add @uploadthing/mime-types@0.3.5-canary.07fd596
pnpm add @uploadthing/nuxt@7.1.8-canary.07fd596
pnpm add @uploadthing/react@7.3.1-canary.07fd596
pnpm add @uploadthing/shared@7.1.8-canary.07fd596
pnpm add @uploadthing/solid@7.3.1-canary.07fd596
pnpm add @uploadthing/svelte@7.3.1-canary.07fd596
pnpm add uploadthing@7.6.1-canary.07fd596
pnpm add @uploadthing/vue@7.3.1-canary.07fd596

@github-actions github-actions Bot removed the release canary Trigger a canary release to npm label May 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

📚 documentation ✅ infra changes implemented The necessary infra changes have been shipped ❗ infra changes required Infra changes required for this change to work sdk @uploadthing/react

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant