Issues with proxying inside middleware
Unanswered
Korat posted this in #help-forum
KoratOP
I have this simple middleware that rewrites the endpoint to an external api. While this works nice for 200 statuses, when I get another status like 401, I get a white page until the retries reach the limit.
middleware.ts
page.tsx
Not sure whether I need to handle this in middleware, like making a fetch to the server and only if the response is ok to rewrite next, I am not sure
middleware.ts
export async function middleware(req: NextRequest) {
const requestHeaders = new Headers(req.headers);
requestHeaders.set("accept-language", "test");
const pathname = req.nextUrl.pathname;
if (pathname.startsWith("/api")) {
const rewriteUrl = new URL(
pathname,
"https://b9f7-2a03-4b80-b900-1440-db4-501a-3c6c-8bdf.ngrok-free.app"
);
console.log(rewriteUrl.href);
return NextResponse.rewrite(rewriteUrl, {
request: { headers: requestHeaders },
});
}
const response = NextResponse.next();
setSecureHeaders(response);
return response;
}
page.tsx
export default async function Home() {
const queryClient = getQueryClient();
await queryClient.prefetchQuery({
queryKey: ["intent-users"],
queryFn: async () => {
const response = ky("http://localhost:3000/api/intern-users").json();
return response;
},
});
return (
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
<h1>Hello World</h1>
<Suspense>
<Users />
</Suspense>
</div>
);
}
Not sure whether I need to handle this in middleware, like making a fetch to the server and only if the response is ok to rewrite next, I am not sure
99 Replies
KoratOP
No error handling that's why
in your page.tsx u are fetching your own "api" and middleware is rewriting everything under api to your external api
?
KoratOP
yes
why would u do that?
KoratOP
Because I need to use Next's server as proxy in between client and that external server, I have some tokens I need to retrieve and forward them
@Korat Because I need to use Next's server as proxy in between client and that external server, I have some tokens I need to retrieve and forward them
if u rewrite the url in your middleware, your actual requested endpoint is also lost
just create a route handler that acts as a middleware if u want to modify headers before sending
but hold on a second
u are fetching it on serverside
this doesnt make sense
so client doesnt exist in this context
its just your server and the external server
@gin just create a route handler that acts as a middleware if u want to modify headers before sending
KoratOP
Yeah, might try this one out, but im worried that I might need to make 4 route handlers (GET, POST, DELETE, PUT), or maybe just one for get requests and for others just use server actions ?
Yeah, while this is just an example, I am using useSuspenseQuery inside Users which is a client component
I wished Next team would come up with some documentation on those cases where an external server is included (best practices, proxying, forward headers)
@Korat Yeah, might try this one out, but im worried that I might need to make 4 route handlers (GET, POST, DELETE, PUT), or maybe just one for get requests and for others just use server actions ?
u can have only 1 handler and just pass it the url u want to request
thats the best practice
KoratOP
yeah but what about the method
just pass in the method aswell
depends on what u want to do
if u really really want to send a PUT request from client, u will then create a route handler accepting PUT
and thats it
KoratOP
inside this route handler, im supposed to use fetch right ?
@gin if u really really want to send a PUT request from client, u will then create a route handler accepting PUT
KoratOP
I won't need a route handler for others than GET, because there are server actions but who knows, I might try it aswell later on
pass in the url u want to fetch, fetch that in the handler and just return the data
this way u eliminate this messy middleware thing
KoratOP
Thanks pal, Ill keep this thread open if I fall into any issue
if u manage to solve the issue with my advice, make sure to mark it as solution :)
KoratOP
Sure will brother
KoratOP
Is it a crime if I call the route handler from a server component 😬
create a function that u can call in your route handler and in your server component
KoratOP
This is where it gets a bit tricky
I need to make it such that it works in server but also works on client too
if we referr to tanstack query / they have a queryOptions
@Korat I need to make it such that it works in server but also works on client too
yeah thats not a problem
actually its easy
create a function that takes every parameter u need and use that in the route handler for your client and directly call it in a rsc
rsc = server component
KoratOP
if im in rsc, i need to read the cookies from next/headers, but if im in client I don't to read those becuase they will be auto included in route handler
you see what I mean ?
yeah correct, so whats the issue in that?
btw
KoratOP
not sure if one function works for both, unless I check if im on server and import next/headers with lazy import
u still need to send the cookies in your route handler
KoratOP
oh wait
@Korat not sure if one function works for both, unless I check if im on server and import next/headers with lazy import
your function will use next cookies directly
KoratOP
sorry my bad
Im overthinking this
client calls route handler directly
👍
just fetch it on the client
KoratOP
while in server i need to take the cookies manually
u still have to get the cookies on the client aswell
its not a actual proxy
u are building the proxy by yourself
so u need to get the cookies and headers in the function and pass that to the fetch
KoratOP
how do I get cookies on client ?
why would u want to do that?
KoratOP
yeah
but lets think about this,
i have a unified queryOptions, that will be included in the prefetch while on rsc and in useSuspenseQuery while on client
if we pass just a function inside the queryFn, it won't work in client because we import next/headers inside this wrapper.
Unless I am thinking of the wrapper uncorrectly
i have a unified queryOptions, that will be included in the prefetch while on rsc and in useSuspenseQuery while on client
if we pass just a function inside the queryFn, it won't work in client because we import next/headers inside this wrapper.
Unless I am thinking of the wrapper uncorrectly
@Korat but lets think about this,
i have a unified queryOptions, that will be included in the prefetch while on rsc and in useSuspenseQuery while on client
if we pass just a function inside the queryFn, it won't work in client because we import next/headers inside this wrapper.
Unless I am thinking of the wrapper uncorrectly
i get it, your queryClient is exposing variables that u automatically use in the fetch request?
@gin i get it, your queryClient is exposing variables that u automatically use in the fetch request?
KoratOP
not sure what you mean here
im trying to understand what the actual issue is here
why cant u just fetch fetch your route handler in your query client?
on the client
on the server its basically just replacing your ky(url).json(); with the function call
KoratOP
I can but I would like to reuse queryOptions
rsc
await queryClient.prefetchQuery(queryOptions)
cc
useSuspenseQuery(queryOptions)
await queryClient.prefetchQuery(queryOptions)
cc
useSuspenseQuery(queryOptions)
ok
do that
KoratOP
I guess this is not possible, I need a wrapper for rsc (just so I don't always get cookies and pass them)
And on client I call the route handler directly
And on client I call the route handler directly
@gin do that
KoratOP
but what would queryFn look like ?
your replace const response = ky(url).json(); with the function call
const response = await yourFunctionThatFetchesExternalEndpoint()
KoratOP
conditionally ?
and the function gets the cookies and uses them
KoratOP
if (isServer) fetchWrapper()
else fetchRouteHandler()
inside route handler we have fetchWrapper()
else fetchRouteHandler()
inside route handler we have fetchWrapper()
uhm
fetchWrapper() in the route handler for the client
fetchWrapper directly in the rsc
fetchWrapper directly in the rsc
i suppose ur queryClient is server only cause it caches something and uses await directly
so directly use fetchWrapper() in your queryFn function
KoratOP
yeah but we need to pass the queryKey, queryFn, ...other stuff, both in prefetchQuery (in rsc) and on client component in useSuspenseQuery
In order to not repeat ourself, tanstack query has this queryOptions function that you can use across the app
In order to not repeat ourself, tanstack query has this queryOptions function that you can use across the app
but in this case it fails when passing the queryFn unless we conditionally tell it to behave correctly
or I don't use queryOptions and I repeat myself,
rsc
await queryClient.prefetchQuery({
queryKey: ['users'],
queryFn: fetchWrapper
})
cc
useSuspenseQuery({
queryKey: ['users'],
queryFn: fetchRouteHandlerDirectly
})
rsc
await queryClient.prefetchQuery({
queryKey: ['users'],
queryFn: fetchWrapper
})
cc
useSuspenseQuery({
queryKey: ['users'],
queryFn: fetchRouteHandlerDirectly
})
you see my point ?