Next.js Discord

Discord Forum

Issues with proxying inside middleware

Unanswered
Korat posted this in #help-forum
Open in Discord
Avatar
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

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

uhm
this looks cursed
Avatar
KoratOP
No error handling that's why
Avatar
in your page.tsx u are fetching your own "api" and middleware is rewriting everything under api to your external api
?
Avatar
KoratOP
yes
Avatar
why would u do that?
Avatar
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
Avatar
@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
Avatar
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
Avatar
@gin just create a route handler that acts as a middleware if u want to modify headers before sending
Avatar
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)
thats the best practice
Avatar
KoratOP
yeah but what about the method
Avatar
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
Avatar
KoratOP
inside this route handler, im supposed to use fetch right ?
Avatar
@gin if u really really want to send a PUT request from client, u will then create a route handler accepting PUT
Avatar
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
Avatar
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 :)
Avatar
KoratOP
Sure will brother
Avatar
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
Avatar
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
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
Avatar
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 ?
Avatar
yeah correct, so whats the issue in that?
btw
Avatar
KoratOP
not sure if one function works for both, unless I check if im on server and import next/headers with lazy import
Avatar
u still need to send the cookies in your route handler
Avatar
KoratOP
oh wait
Avatar
KoratOP
sorry my bad
Im overthinking this
client calls route handler directly
Avatar
👍
just fetch it on the client
Avatar
KoratOP
while in server i need to take the cookies manually
Avatar
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
Avatar
KoratOP
how do I get cookies on client ?
Avatar
why would u want to do that?
Avatar
oh sorry i didnt say it correctly
i wanted to say route handler
not client
Avatar
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
Avatar
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
Avatar
KoratOP
I can but I would like to reuse queryOptions
rsc

await queryClient.prefetchQuery(queryOptions)

cc
useSuspenseQuery(queryOptions)
Avatar
ok
do that
Avatar
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
Avatar
@gin do that
Avatar
KoratOP
but what would queryFn look like ?
Avatar
your replace const response = ky(url).json(); with the function call
const response = await yourFunctionThatFetchesExternalEndpoint()
Avatar
KoratOP
conditionally ?
Avatar
and the function gets the cookies and uses them
Avatar
KoratOP
if (isServer) fetchWrapper()
else fetchRouteHandler()

inside route handler we have fetchWrapper()
Avatar
uhm
fetchWrapper() in the route handler for the client
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
Avatar
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
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
})
you see my point ?