## Summary When using UploadThing v7 with Elysia framework, the CORS middleware applied globally in the main app instance does not work for the UploadThing route handler. This requires manually applying CORS directly to the UploadThing controller, which results in duplicate CORS configuration. ## Environment | Component | Version | |-----------|---------| | uploadthing | 7.7.4 | | @uploadthing/react | 7.3.3 | | elysia | 1.4.5 | | @elysiajs/cors | 1.4.0 | | bun | 1.3.2 | ## Problem Description We are using Elysia as our backend framework with `@elysiajs/cors` plugin for handling CORS. The CORS plugin is applied globally in the main app instance before mounting the UploadThing controller. However, requests to the UploadThing endpoint fail with CORS errors: ``` Access to fetch at 'http://localhost:3333/api/uploadthing?actionType=upload&slug=avatarUploader' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status. ``` The error message indicates that the OPTIONS preflight request is not being handled correctly. ## Steps to Reproduce ### 1. Setup Elysia with global CORS ```typescript // main.ts import { cors } from "@elysiajs/cors" import { Elysia } from "elysia" import { uploadthingController } from "./uploadthing.controller" const app = new Elysia() .use( cors({ origin: ["http://localhost:3000"], credentials: true }) ) .use(uploadthingController) .listen(3333) ``` ### 2. Create UploadThing controller without local CORS ```typescript // uploadthing.controller.ts import { Elysia } from "elysia" import { createRouteHandler, createUploadthing, type FileRouter } from "uploadthing/server" const f = createUploadthing() export const uploadRouter = { avatarUploader: f({ image: { maxFileSize: "4MB", maxFileCount: 1 } }).onUploadComplete(async ({ file }) => { return { url: file.ufsUrl, key: file.key } }) } satisfies FileRouter export type OurFileRouter = typeof uploadRouter const handlers = createRouteHandler({ router: uploadRouter, config: { token: process.env.UPLOADTHING_TOKEN } }) export const uploadthingController = new Elysia({ prefix: "/api/uploadthing" }) .all("/*", async (ev) => { const response = await handlers(ev.request) return response }) .all("/", async (ev) => { const response = await handlers(ev.request) return response }) ``` ### 3. Setup frontend client ```typescript // Frontend code import { generateUploadButton } from "@uploadthing/react" const UploadButton = generateUploadButton({ url: "http://localhost:3333/api/uploadthing" }) // Use the UploadButton component console.log(res)} onUploadError={(error) => console.error(error)} /> ``` ### 4. Attempt to upload a file The upload fails with a CORS error on the preflight OPTIONS request. ## Expected Behavior The global CORS middleware should handle all routes, including the UploadThing endpoint. The preflight OPTIONS request should return 204 with the appropriate CORS headers, and the actual POST request should succeed. ## Actual Behavior The CORS headers are not applied to responses from the UploadThing route handler. The preflight request fails because it does not receive the expected CORS headers. ## Root Cause Analysis The issue appears to be that when the UploadThing `createRouteHandler` returns a `Response` object directly, Elysia does not apply its global middleware hooks to that response. In Elysia, when a handler returns a plain JavaScript object, Elysia transforms it into a `Response` internally and applies all middleware hooks during this process. However, when a handler returns a `Response` object directly (as the UploadThing handler does), Elysia sends it as is without applying the middleware transformations. This means the CORS headers that would normally be added by the `@elysiajs/cors` plugin are never applied to responses from the UploadThing handler. ## Workaround The workaround is to apply the CORS plugin directly to the UploadThing controller: ```typescript import { cors } from "@elysiajs/cors" import { Elysia } from "elysia" import { createRouteHandler, createUploadthing, type FileRouter } from "uploadthing/server" // ... router setup ... export const uploadthingController = new Elysia({ prefix: "/api/uploadthing" }) .use( cors({ origin: ["http://localhost:3000"], credentials: true }) ) .all("/*", async (ev) => { const response = await handlers(ev.request) return response }) .all("/", async (ev) => { const response = await handlers(ev.request) return response }) ``` This works because when CORS is applied directly to the controller, it intercepts the OPTIONS preflight request before it reaches the UploadThing handler and responds with the appropriate CORS headers. ## Questions 1. Is this the expected behavior when using UploadThing with frameworks that return `Response` objects directly? 2. Should the UploadThing handler include CORS headers in its responses by default or as a configuration option? 3. Is there a recommended way to integrate UploadThing with Elysia that avoids this issue? ## Additional Context Other endpoints in our application (that return plain objects instead of `Response` objects) work correctly with the global CORS middleware. Only the UploadThing endpoint requires the duplicate CORS configuration. This issue may also affect other frameworks or adapters that use the generic `createRouteHandler` from `uploadthing/server` and rely on global CORS middleware.