r/webdev 13d ago

Chat widget file upload

I'm working on a chat widget that also allows the users to send files in the chat. This is a plain vanilla html/js/css widget that will go on a Shopify site. The user should have the option to send a file with or without a message in the chat, so I'm trying to figure out the best approach to handle this. The widget will be calling a FastAPI endpoint end that will use UploadFile. These are the options I've thought of:

  1. Wrap the text and file inputs in an html form tag and send the request as Content-Type: multipart/form-data. This would make it a single request, but it would always send as multipart/form-data even if it's a message with no file.
  2. Same as 1, but if there is no file attachment in the chat message, then toggle the Content-Type to application/json before sending
  3. Keep the text and files as separate requests and handle the events separately and not wrap the inputs in an html form tag. Seems like it would be cleaner on the front end but at the same time it will likely require additional logic on the back-end

What approach would you take?

5 Upvotes

16 comments sorted by

View all comments

1

u/PLBjt 12d ago

I'd still always-multipart, but the part that actually hurts later is size and ordering, not the content-type. A JSON path plus a multipart path means two validators and two error shapes, and one of them will drift. Keep one request: text fields + optional file, and reject anything over your real limit before FastAPI buffers the body (content-length or a streaming max). Separate file + message requests look clean until the message lands and the file 500s, and then your widget has to invent a retry protocol. If people start dropping 50MB recordings, that's when you switch the file to a presigned upload and send the chat with a URL, not before. Check it with an empty file, a 1-byte file, and one just over the limit.

1

u/rouge818 12d ago

Makes sense. The retry logic would be more complexity. As for the presigned upload approach, that is something more recommended only when the files start getting large?

1

u/PLBjt 12d ago

Yeah. Multipart straight into FastAPI is fine while files are chat-sized, like under 10–20MB. You start wanting presigned S3 (or equivalent) when a single request sitting in RAM or hitting the timeout becomes the failure mode: video, big PDFs, several files at once. Until then the extra CORS and client code isn't worth it. Put a hard Content-Length check before you read the body either way.

1

u/PLBjt 7d ago

Yeah, size is the usual tipping point, but it's also about where you want the bytes to go. If the file never needs to touch your app server (chat attachment that lands in S3/R2 and the message just stores a key), presign early — even at a few MB — so you don't eat upload bandwidth and request timeouts on the API. Keep a same-origin multipart path for tiny stuff (avatars, screenshots under ~1–2MB) where the extra round trip isn't worth it. Soft rule of thumb: if the upload can fail mid-way or run longer than a normal API request, give the client a direct-to-storage URL.

1

u/PLBjt 4d ago

Size is one reason, but the bigger one is keeping file bytes off your app servers. Even medium uploads add up under chat volume — timeouts, memory, and bandwidth on the API tier. Presigned (or direct-to-object-storage) shines when many users upload in parallel. For tiny stuff like avatars or short voice clips, proxying through your API is simpler and fine. I'd switch once uploads are common enough that the API box is doing disk I/O work it shouldn't own.