Return Blob from server action
Answered
Korat posted this in #help-forum

KoratOP
Is it possible in Next 14 to return a blob from server action to client.
I believe this feature works in Next15 but it’s not working in 14. I get this message saying that only plain objects and some other primitives can be returned.
I believe this feature works in Next15 but it’s not working in 14. I get this message saying that only plain objects and some other primitives can be returned.
93 Replies

KoratOP
Not sure if there was a fix in NextJs 15, can't seem to find the PR

@Korat Is it possible in Next 14 to return a blob from server action to client.
I believe this feature works in Next15 but it’s not working in 14. I get this message saying that only plain objects and some other primitives can be returned.

what are you trying to archive? Why do you want to have a blob? What#s your goal for you app?

KoratOP
I want to download an excel file from an external api, i assume the only possible way in next 14.2 is to use a route handler
But still in Next 15 the blob can be returned from the server action which is great but im on 14.2 sadly
But still in Next 15 the blob can be returned from the server action which is great but im on 14.2 sadly

American black bear
use s3 for serving files

@Korat I want to download an excel file from an external api, i assume the only possible way in next 14.2 is to use a route handler
But still in Next 15 the blob can be returned from the server action which is great but im on 14.2 sadly

yea, that you want to download something was clear for me. What do you want to do with this file after you downloaded it?

KoratOP
Just download it on my computer ?
I ended up using a route handler but just curios if next team did something regarding blobs in server action on v15

@Korat Just download it on my computer ?

when you just want to download a file to your computer, why do you want to do it with a route handler or server action?
Wouldn't it be waaaay easier to open the link via
Wouldn't it be waaaay easier to open the link via
window.open
or via a <Link href='https://yourlink.com/' />
tag?
KoratOP

@B33fb0n3 when you just want to download a file to your computer, why do you want to do it with a route handler or server action?
Wouldn't it be waaaay easier to open the link via window.open or via a <Link href='https://yourlink.com/' /> tag?

KoratOP
Because I need to have an endpoint for this, its either server action or route handler, just the thing that return blob in server action works in next 15 but doesnt in 14

@Korat Because I need to have an endpoint for this, its either server action or route handler, just the thing that return blob in server action works in next 15 but doesnt in 14

just to get this right you want to do something like this:
to get a file instead of just using the file url itself:
Is that correct?
https://example.com/api/your/file/route
to get a file instead of just using the file url itself:
https://example.com/your/file.xls
Is that correct?

KoratOP
The file is excel
the api should return a file as response

@Korat the api should return a file as response

yes to download the file, right?

KoratOP
yes
using a server action in next15
const response = fetch(externalapi that returns a file)
return response.blob()
const response = fetch(externalapi that returns a file)
return response.blob()

@Korat yes

why do you want to build an extra api route (or server action) just to download a file?

KoratOP
thats a good question, because I use NextJs as bff

as bff?

KoratOP
client requests need to go through next server
backend for frontend pattern

so like the server must allow that the user can see the file?

KoratOP
what do you mean

you said:
So I assume the request would look like this:
Client --- Request ---> NextJs Server --- Serve File ---> Client
And the only reason I see right now is because the file shouldn't be directly public
client requests need to go through next server
So I assume the request would look like this:
Client --- Request ---> NextJs Server --- Serve File ---> Client
And the only reason I see right now is because the file shouldn't be directly public

KoratOP
Yes
you are correct
client - next server - external server
left to right and back

and let me guess: the external server need some kind of auth to serve the file, right?

KoratOP
yes

and only your nextjs server knows this secret, right?

KoratOP
yeah a token saved in cookies
thats why im going to next server actually, to receive it and request that file in that external server

@Korat yeah a token saved in cookies

does the client knows the secret?
saved in cookies

KoratOP
nope
just so I dont take your time, i managed to make it work using a route handler, im just confused as to why v15 allows to return a blob from server action to client while v14 doesnt

KoratOP
those cookies have secure flags which cannot be accessed using javascript
httponly and secure

the cookies will be attached to the server request, right?

KoratOP
Depends which server you are asking for, the external server has a different domain

hmm got it. When your nextjs request something from your external server, what does your external server return? An url to the excl file?

KoratOP
no its a file, I would be more than happy if it returned a url tbh hahahahahah

uff... let me guess, you have no control over the external backend?

KoratOP
I like the way you think
yes thats a separate team
but on second thoughts they could make it so it returned an url that points to the storage but I think they generate this excel on the fly

ah that sucks. They generating the file so this file need to be stored somewhere, right? Or is it just "saved" inside the request

KoratOP
By them?
Not sure if they need to save it somewhere, maybe it should be generated with fresh data all the time
Im not sure though, but we can assume this is the way they decided to do it

yea, they can either replace the data or generate a new file everytime and return you the file url. Tbh that sounds a bit like they using excl tables to have a "database"

KoratOP
Yeah, I believe they need to calculate the data everytime i click that generate excel button
so, no need to save it on a storage and return the url IMO
Would be nice if a guy from nextjs approves that returning a blob from server action wouldnt be able in v14 but is in 15

@Korat so, no need to save it on a storage and return the url IMO

yea, makes sense. Are you using next14 or next15 rn?

KoratOP
14 sadly, can’t really upgrade to 15 cause of the breaking changes
but im happy with the route handler solution
thats a bit more work but it is what it is

yea, I guess that's the only thing that you can do. Would be way better if your other team supports you a bit more and "helps" you a bit there, but I guess that's not possible

KoratOP
But you agree with me that v15 supports returning blob from server action while v14 doesnt just so I don’t think im tripping😂😂

@Korat But you agree with me that v15 supports returning blob from server action while v14 doesnt just so I don’t think im tripping😂😂

haha I just tried to find other solutions for you, that's why I asked so many details sorry xD
It still was a fun thread 🤣
Thanks for being that communicative
It still was a fun thread 🤣
Thanks for being that communicative

KoratOP
Tbh your questions were spot on, you need to check the bff approach though lol
knowing that a lot of apps have an external backend that depends on
Remix has a section just for that
We depend heavily on this approach in order to benefit from nextjs server goods
If you ask me, application that hides behind auth are better built using plain react but I chose to follow the meta framework hype in 2024

@Korat We depend heavily on this approach in order to benefit from nextjs server goods

Interesting approach. I like to serve mostly everything through nextjs and there are things like database, files, ... I am using external services. Like:
S3 for file save (or R2)
Cloudflare, Cloudfront or BunnyCDN for serving files
My VPS for database
...
Like that none of the services knows each other and they can match perfectly together and every service can do what it can do best
S3 for file save (or R2)
Cloudflare, Cloudfront or BunnyCDN for serving files
My VPS for database
...
Like that none of the services knows each other and they can match perfectly together and every service can do what it can do best

KoratOP
Yeah but those give you apis that fits great with nextjs right?

@Korat If you ask me, application that hides behind auth are better built using plain react but I chose to follow the meta framework hype in 2024

yea, I think that would make more network request instead of less:
Client -> NextJs Server -> External Server (2 requests)
Instead of:
Client -> External Server (1 request)
Normally the client auth himself via token or session or whatever and if there isn't anything, the request will be declined
Client -> NextJs Server -> External Server (2 requests)
Instead of:
Client -> External Server (1 request)
Normally the client auth himself via token or session or whatever and if there isn't anything, the request will be declined

KoratOP
Im aware of 2 round trips, not a fan of it though but everything has trade offs these days

@Korat Yeah but those give you apis that fits great with nextjs right?

no not really. They all depend on web standards so they can be integrated everywhere. S3 upload is just a post request with the file. BunnyCDN just points to the S3 origin and provides just the url. My database just creates the database and handles connections with queries.
None of these services are "special" or "especially made" for nextjs
None of these services are "special" or "especially made" for nextjs

@Korat Yeah but those give you apis that fits great with nextjs right?

KoratOP
In my case the external server is .NET so yeah bff is the only way😂😂
but you use an orm right?
think my situation like so.
I dont have control over anything, I just know the api endpoints that I need to communicate with and leave the rest to the other team
I dont have control over anything, I just know the api endpoints that I need to communicate with and leave the rest to the other team

@Korat In my case the external server is .NET so yeah bff is the only way😂😂

yea, sometimes that happens. For one of my customers it's also .NET backend, but he is very supportive. If I ask him "can you return me a url to the file instead of the buffer", he directly does that. I wish every developer could experience this 🤣

@Korat but you use an orm right?

yes drizzle. This one is also not directly made for nextjs. It also uses just postgres standards these can be easily integrated with nodejs

@Korat think my situation like so.
I dont have control over anything, I just know the api endpoints that I need to communicate with and leave the rest to the other team

that sounds like you are just building the frontend, but want to be save when they somewhen say "can you add this and that as well" and then you need your own backend xD

KoratOP
thats good bro, but I dont have this luxury, but even with 2 round trips the communication seems to be very fast

@B33fb0n3 that sounds like you are just building the frontend, but want to be save when they somewhen say "can you add this and that as well" and then you need your own backend xD

KoratOP
spot on again😂 We help them to generate pdf with react pdf cause they hate it, maybe every backend does hahahahahaha

KoratOP
So i still believe that in those circumstances we didn’t have to follow the metaframework hype and stick to react with tanstack router but we switched anyways.
But we needed some SEO support and the next thing I know we are all in nextjs whether its an app behind auth or just a public app hahahahaha
But we needed some SEO support and the next thing I know we are all in nextjs whether its an app behind auth or just a public app hahahahaha

@B33fb0n3 haha I hate it as well 🤣

KoratOP
told ya hahaha
But its nice having the possibility to do backend stuff too, you never know when you need it

@Korat So i still believe that in those circumstances we didn’t have to follow the metaframework hype and stick to react with tanstack router but we switched anyways.
But we needed some SEO support and the next thing I know we are all in nextjs whether its an app behind auth or just a public app hahahahaha

yea true. nextjs helps there a lot and it's easy to rank on google when the correct content is there

@Korat But its nice having the possibility to do backend stuff too, you never know when you need it

hopefully they never ask for the source code because they have another frontend dev and this "other frontend dev" just knows react or something only frontend. Then you are f***t 🤣

@B33fb0n3 hopefully they never ask for the source code because they have another frontend dev and this "other frontend dev" just knows react or something only frontend. Then you are f***t 🤣

KoratOP
hahahahaha you tell me bro, I spent the whole year figuring out the best approach to use nextjs with an external backend
Actually the backend team are guys from the same company, so no worries for that lol
Actually the backend team are guys from the same company, so no worries for that lol

haha perfect, then you are at least prepared for that 👏
Answer

KoratOP
Yeah, I am a technical leader so I try to transfer the knowledge to all of the front team, I understand those who have no idea about backend and jump to Nextjs

yea, all the best with your project ^^

KoratOP
plural😂😂♥️
Still gotta find the pull request for the blob fix hahahahahah