You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Better File Upload/Download operations in oRPC thanks to builtin integration of TUS #482
For files larger than 10MB, it is recommended to use a dedicated solution for performance and reliability.
From my understanding, it is not recommended for "big" files, because under the hood, it encodes/decodes in base64 (haven't tested it)?
Even, file upload is much more than only 1 API request, it's not the same thing as basic JSON api call. Performance and resumable file uploads are crucial + good UX client side to show how much kb has been uploaded and much need to be uploaded still etc.
While, we can already use @tus/server in combination with oRPC, I believe there might be a better way that keep type-safety, zod goodness and simplicity while preserving tus goodness too.
Would be great to define oRPC route as "tus route" and then call them with the RPCClient like normal routes with more features like tus-js-client, for example:
The good thing, is that we can have a onProgress and regularly tell the user, how much bytes uploaded, how much still needed, percentage.
Also we have out of the box, resumability so for example, if the internet connection of the user is bad, and is interrupted, they can try to upload again the file, without the need of uploading the file from scratch, especially useful for big files, or really bad connection, like mobile network.
We could have a way to do it, with oRPC, the same type-safety, and APIs to define middleware.
We don’t use Base64 for file uploads/downloads in oRPC. Instead, we transmit raw binary data + optionally combined with multipart/form‑data when needed (it's good as standard file upload/download - only missing dedicated features for large file handling)
This feature could be implemented as a plugin. I'll make a note of it and evaluate the approach soon.
Reacted by Théo LUDWIG, Muhammad Arslan, Zane and Muhammad Yusuf
Meanwhile, I think, it would be great to elaborate in the documentation to why "it is not recommended", is it because it's doing 1 HTTP request for the whole file? Also, until there is a better solution in oRPC builtin, maybe the documentation should link to better solution like TUS.
This is a great idea! Already using TUS with uppy for the front-end in my tRPC / fastify stack and loving it. Not difficult to setup with fastify but this would provide a very nice file upload solution out of the box with oRPC if it could be done.
Reacted by Định Lê, Jacopo Marrone and Théo LUDWIG
I'm trying to understand how file uploads are represented in the generated OpenAPI spec.
I have the following API contract defined with Zod:
constCreateSourceInputBodySchema=z.object({content: z.file().max(MAX_DIRECT_UPLOAD_BYTES).meta({description:
"The artifact file to store. Capped at 10MB; use POST /sources/upload for larger artifacts.",format: "binary",}),name: z.string().trim().nonempty().max(256).optional().meta({description:
"Human-friendly source name. Defaults to the uploaded file name when omitted.",}),origin: z.string().trim().nonempty().max(64).meta({description:
'Free-form provenance label for where the artifact came from. Normalized to lowercase kebab-case on write (e.g. "Plaude AI" → "plaude-ai").',}),type: SourceTypeSchema.meta({description: "The kind of artifact the source holds",}),});
My server implements this contract and expects a multipart/form-data request:
Everything works correctly at runtime. However, when I generate an OpenAPI spec with the oRPC OpenAPI plugin and import the resulting spec.json into clients such as Postman or Scalar, the endpoint is not recognized as accepting multipart/form-data, and the content field is not treated as a file upload. It's treated as a string input as you described above. I want the default to be multipart/form-data.
Am I missing additional metadata or configuration that's required for the OpenAPI generator to emit a multipart request body? Or is z.file() currently not translated into the appropriate OpenAPI schema (type: string, format: binary under multipart/form-data)?
What is the recommended way to define this contract so that OpenAPI clients correctly infer that the endpoint accepts file uploads?
@JSteunou Could you point to the exact part of the generated OpenAPI spec that's causing the issue, and explain what you expect instead? I can't control how third-party tools interpret the spec. My focus is on generating a valid OpenAPI 3.1 specification. Personally, I believe this is a limitation of the tool you're using.
Activity
dinwwwh commentedon May 10, 2025
franciscohermida commentedon May 11, 2025
have been spending time figuring out the best way to handle files with orpc as well or if I just have a separate handlers
theoludwig commentedon May 11, 2025
Meanwhile, I think, it would be great to elaborate in the documentation to why "it is not recommended", is it because it's doing 1 HTTP request for the whole file? Also, until there is a better solution in oRPC builtin, maybe the documentation should link to better solution like TUS.
tresorama commentedon May 29, 2025
@theoludwig How tus handle multiple file upload?
One request per file ?
theoludwig commentedon May 29, 2025
@tresorama TUS, send multiple requests based on the file size. Multiple requests can be sent even for a single file.
tresorama commentedon May 29, 2025
Thanks , so is not possible to have one endpoint for a form that can send both json data and files?
Tus endpoints are files only ?
JSteunou commentedon Jun 2, 2025
This is a great idea! Already using TUS with uppy for the front-end in my tRPC / fastify stack and loving it. Not difficult to setup with fastify but this would provide a very nice file upload solution out of the box with oRPC if it could be done.
dinwwwh commentedon Jun 26, 2025
related: https://orpc.unnoq.com/docs/advanced/extend-body-parser
francisco-barros-codes commentedon Jul 22, 2026
I'm trying to understand how file uploads are represented in the generated OpenAPI spec.
I have the following API contract defined with Zod:
My server implements this contract and expects a
multipart/form-datarequest:Everything works correctly at runtime. However, when I generate an OpenAPI spec with the oRPC OpenAPI plugin and import the resulting
spec.jsoninto clients such as Postman or Scalar, the endpoint is not recognized as acceptingmultipart/form-data, and thecontentfield is not treated as a file upload. It's treated as a string input as you described above. I want the default to bemultipart/form-data.Am I missing additional metadata or configuration that's required for the OpenAPI generator to emit a multipart request body? Or is
z.file()currently not translated into the appropriate OpenAPI schema (type: string,format: binaryundermultipart/form-data)?What is the recommended way to define this contract so that OpenAPI clients correctly infer that the endpoint accepts file uploads?
dinwwwh commentedon Jul 22, 2026
@JSteunou Could you point to the exact part of the generated OpenAPI spec that's causing the issue, and explain what you expect instead? I can't control how third-party tools interpret the spec. My focus is on generating a valid OpenAPI 3.1 specification. Personally, I believe this is a limitation of the tool you're using.
(Please open a new issue for it)
dinwwwh commentedon Aug 15, 2026
In v2 we can use
import { TmpFileUploadHandlerPlugin } from '@orpc/node'to increase upload ability!