quarkiverse / quarkus-quinoa

Quinoa is a Quarkus extension which eases the development, the build and serving of single page apps (built with NodeJS: React, Angular, …) alongside Quarkus . It is possible to use it with a Quarkus backend in a single project
Apache License 2.0
79 stars 38 forks source link

NextJS: PageRouter issue using direct URL #727

Open UbiquitousBear opened 1 month ago

UbiquitousBear commented 1 month ago

Describe the bug

When I navigate directly to any non-root URL e.g. http://localhost:8080/components/[id] or http://localhost:8080/components/, the contents of http://localhost:8080/ (ie src/pages/index.tsx is shown. If it try the same request over the nextjs dev server directly, ie http://localhost:3000/components/[id] the routing works as expected.

Inspecting the state of next/router shows that the path is picked up, but nothing else is. Unsure whether this is a next issue or a quinoa issue.

Quinoa version

2.4.3

Quarkus version

3.12.3

Build / Runtime

Next.js

Package Manager

PNPM

Steps to reproduce the behavior

next14-quinoa.zip

Unzip and run mvn quarkus:dev and navigate to http://localhost:8080 it uses Next App Router and the dynamic slugs work fine.

However naivgate directly to http://localhost:8080/post/2020/first-post/with/catch/all/routes and it loses the route.

Expected behavior

No response

melloware commented 1 month ago

@UbiquitousBear have you read the section on SPA: https://docs.quarkiverse.io/quarkus-quinoa/dev/advanced-guides.html#spa-routing

See both the NOTE and the WARNING.

UbiquitousBear commented 1 month ago

@melloware

Note: By default, Quinoa will ignore quarkus.resteasy-reactive.path, quarkus.resteasy.path and quarkus.http.non-application-root-path path prefixes. You can specify different path prefixes to ignore using quarkus.quinoa.ignored-path-prefixes.

Warning: Currently, for technical reasons, the Quinoa SPA routing configuration won’t work with RESTEasy Classic. Instead, you may use a workaround (if your app has all the rest resources under the same path prefix):

To clarify, I'm using the graphql extension, which exposes /graphql, so I don't think the cases fall under either, unless /graphql should be defined under quarkus.quinoa.ignored-path-prefixes ?

melloware commented 1 month ago

Yeah I am wondering the same thing.

Any way you can try it out or put together a small runnable reproducer?

melloware commented 1 month ago

Also did you enable spa routing? A runnable reproducer will help figure it out.

UbiquitousBear commented 1 month ago

@melloware I've enabled SPA routing, whilst I can't isolate the example (simply due to lack of time this week), I am more than happy to push the whole thing to a private GH repo if that helps?

melloware commented 1 month ago

Sure it might help me if I can look at it and suggest some changes.

UbiquitousBear commented 1 month ago

@melloware invited :)

melloware commented 1 month ago

Thanks I will check it out tomorrow

melloware commented 1 month ago

@UbiquitousBear try this. next14-quinoa.zip

Unzip and run mvn quarkus:dev and navigate to http://localhost:8080 it uses Next App Router and the dynamic slugs work fine.

This proves Quinoa and SPA is working.

you might want to check if its all that Atlassian Code or other things you have going on in your complex project.

UbiquitousBear commented 1 month ago

@melloware if you navigate directly to http://localhost:8080/post/2020/first-post/with/catch/all/routes in your browser, does it still work?

When I go to that URL directly, I see:

[Home](http://localhost:8080/)
[About](http://localhost:8080/about)
[First Post](http://localhost:8080/post/2020/first-post/with/catch/all/routes)
[Second Post](http://localhost:8080/post/2020/second-post/with/catch/all/routes)
Hello World!
melloware commented 1 month ago

Ahhh it works when i click the Next links but not directly do the URL in the browser. Let me look.

melloware commented 1 month ago

Interesting so when you click the link here is what Next Generates:

http://localhost:8080/_next/static/chunks/pages/post/%5B...slug%5D.js

Which is not the same as

http://localhost:8080/post/2020/first-post/with/catch/all/routes
melloware commented 1 month ago

Assigned to @ia3andy i am not quite sure how this route is being lost.

melloware commented 1 month ago

@all-contributors add @UbiquitousBear for bug

allcontributors[bot] commented 1 month ago

@melloware

I've put up a pull request to add @UbiquitousBear! :tada:

UbiquitousBear commented 1 month ago

@melloware trying with 2.4.4, still not fixed?

melloware commented 1 month ago

No. No fix was made I only tweaked how it discovers NextJS but not your issue which is why it's still open.

UbiquitousBear commented 1 month ago

Ah ok - I wonder if it's something to do with how rewriting is forwarded. I believe the same happens when I build the project into a jar for deployment.

melloware commented 1 month ago

Yep it's something going on with SPA routing.

melloware commented 1 month ago

I am debugging this. I think I am closer to seeing what is wrong.

melloware commented 1 month ago

Here is what I have debugged:

[io.qua.qui.QuinoaSPARoutingHandler] (vert.x-eventloop-thread-2) Quinoa checking SPA request '/post/2020/second-post/with/catch/all/routes'
[io.qua.qui.QuinoaSPARoutingHandler] (vert.x-eventloop-thread-2) Mount Point: '/'  Route Path: '/'
[io.qua.qui.QuinoaSPARoutingHandler] (vert.x-eventloop-thread-2) Quinoa is re-routing SPA request '/post/2020/second-post/with/catch/all/routes' to '/'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/chunks/webpack.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-3) Quinoa is forwarding: '/_next/static/development/_buildManifest.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/chunks/pages/_app.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-3) Quinoa is forwarding: '/_next/static/chunks/pages/index.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/development/_ssgManifest.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/chunks/react-refresh.js'
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/chunks/main.js'
[io.net.han.cod.htt.web.WebSocketServerHandshaker] (vert.x-eventloop-thread-2) [id: 0xf37380dd, L:/127.0.0.1:8080 - R:/127.0.0.1:49346] WebSocket version V13 server handshake
[io.net.han.cod.htt.web.WebSocketServerHandshaker] (vert.x-eventloop-thread-2) WebSocket version 13 server handshake key: nsM7SH5Cyulob732tXFtWA==, response: DQ/ZZpL7GZ78wggZGGsDlPWTqoc=
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Server Connected: 127.0.0.1:3000/_next/webpack-hmr
[io.net.han.cod.htt.web.WebSocketClientHandshaker13] (vert.x-eventloop-thread-2) WebSocket version 13 client handshake key: aoCqCGY1pUICRlB533ZAkA==, expected response: r5nmJp82/Efxbzl29S9GEE1HaX4=
[io.ver.cor.htt.imp.Http1xClientConnection] (vert.x-eventloop-thread-2) WebSocket handshake complete
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Client Connected: 127.0.0.1:3000/_next/webpack-hmr
[io.qua.qui.QuinoaDevProxyHandler] (vert.x-eventloop-thread-2) Quinoa is forwarding: '/_next/static/development/_devMiddlewareManifest.json'
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Client message: {"action":"sync","hash":"172eea221fcc05a4","errors":[],"warnings":[],"versionInfo":{"staleness":"fresh","installed":"14.2.5"}}
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Server message:  {"event":"client-success","clientId":1722003263354}
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Server message:  {"event":"span-end","startTime":1722003263180,"endTime":1722003263398,"spanName":"navigation-to-hydration","attributes":{"pathname":"/post/2020/second-post/with/catch/all/routes","query":""}}
[io.qua.qui.QuinoaDevWebSocketProxyHandler] (vert.x-eventloop-thread-2) Quinoa Dev WebSocket Server message:  {"event":"ping","page":"/"}

This websocket message back from Next is interesting.

Quinoa Dev WebSocket Server message:  {"event":"span-end","startTime":1722003263180,"endTime":1722003263398,"spanName":"navigation-to-hydration","attributes":{"pathname":"/post/2020/second-post/with/catch/all/routes","query":""}}
UbiquitousBear commented 1 month ago

Line 3, why is quinoa rerouting to /?

melloware commented 1 month ago

@UbiquitousBear its explained right here: https://docs.quarkiverse.io/quarkus-quinoa/dev/advanced-guides.html#spa-routing

It looks like its successfully being sent to Next. But the response from Next is not the page it looks like somehow it stays routed to / but the WebSocket response from Next seems to indicate it knows the route happened.

UbiquitousBear commented 1 month ago

Could this be a nextjs bug? But wonder why it can't be reproduced using the dev server....

melloware commented 1 month ago

I don't think so i think its just a matter of figuring out why Quinoa SPA handler is not handling NextJS 14 AppRouter. I used to have it working it older NextJS versions I think 12. But AppRouter is newer and NextJS seems to be changing every day.

UbiquitousBear commented 1 month ago

To clarify, the example (and where I'm see the issue) uses the Pages Router, not the App Router...

ia3andy commented 2 days ago

I see a mention of hydration, are you sure there is no server side rendering in there?

UbiquitousBear commented 2 days ago

I see a mention of hydratation, are you sure there is no server side rendering in there?

what are you defining as hydration? Hydration is happening at some level, always does in React applications.

ia3andy commented 2 days ago

@UbiquitousBear AFAIK hydration is happening only when the server is doing part of the work which is not the case for plain SPA no?

UbiquitousBear commented 2 days ago

@UbiquitousBear AFAIK hydration is happening only when the server is doing part of the work which is not the case for plain SPA no?

That's one definition of hydration when applied to the App Router. Hydration as-is, in client side generated apps still refers to attaching event listeners to the static HTML, making it dynamic at some level.

https://github.com/vercel/next.js/blob/cf62dccd850ce48429955463205dd0105e83ddbd/packages/next/src/client/index.tsx#L569 is calling the markHydrateComplete function which generates the span.