qwik-city: page routes & page endpoints don't operate correctly, and also should work like / double as data endpoints w/ pages
Qwik Version
latest
Operating System (or Browser)
browser
Node Version (if applicable)
No response
Which component is affected?
Qwik City
Current Behavior
Currently in production (npm run preview) page routes will generate a page when navigated to directly or via SPA. When implementing methods inside the page route (onGet, onPost, onPut, onDelete, etc.) it can double as a data endpoint and you can useEndpoint() in the page itself to fetch from these endpoints which will differ depending on client or server. On the client useEndpoint uses a fetch() to the page route itself for JSON data much like you would to a data endpoint.
However, all data requests to page routes (accept: application/json) automatically return extra JSON in the form of:
{
isStatic: boolean,
prefetch: string[],
body: <JSON returned from implemented handlers>
}
This is returned on ANY requests ( w/ accept: application/json) even to HTTP methods that aren't implemented, just without the body field, along with a 200 OK status code; whereas data endpoints return a 405 Method Not Allowed.
I am not entirely sure what the isStatic and prefetch fields are for but I assume it's SPA or component related or something? However, the useEndpoint functionality when making a client-side fetch() it prepares the URL as location.href + '/q-data.json', this could be useful below?
Another issue is pages will also return 200 OK as HTML when requested with any HTTP method (without accept: application/json), such as POST or with CURL for methods that aren't browser navigable (PUT, DELETE, etc), even when those other HTTP methods aren't implemented.
Proposed Behavior
- Pages should act like data endpoints when
accept: application/jsonis part of the request headers and return data as returned by handler implementations directly without extra stuff. - Requests to
pageRoute + '/q-data.json'could maintain the current behavior with extraisStaticandprefetchas needed. Same for all HTTP methods? TBD - All data requests to methods on page endpoints (
accept: application/json) with HTTP methods that aren't implemented or valid should return405 Method Not Allowedand either an error page or error JSON (or nothing). Along with anallow: <METHODS>header. - Pages requested WITHOUT
accept: application/jsonshould ALSO return a405w/ an error page instead when requested with an unimplemented method (like POST, DELETE, etc) or invalid methods, andallow: <METHODS>header instead. (this is how www.google.com works as an example) - Methods that aren't browser navigable (DELETE, PUT, PATCH, etc.) that ARE implemented but requested WITHOUT
accept: application/jsonshould be thought about how to handle. Return JSON anyway? or potentially you could return HTML on these for usecases outside direct browser navigation, but it might not be the same HTML you would want to return via GET or POST. TBD?
Usecase Example
Simple usecase for using page routes as data endpoints. You have a page route /products which produces a page that lists products and you can click-through to view product details on a page route /products/[skuId]. Both of these page routes would have internal endpoints to fetch their respective data, however delete buttons on the /products page would send a DELETE request to /products/[skuId] which would implement the onDelete handler and return JSON data indicating success or failure, etc. As a further example, another page could GET /products or /products/[skuId] for JSON data to display some data on a product elsewhere, etc.
Actual Behaviour
n/a
Additional Information
No response
I think named endpoints, something like useEndpoint('/products') would solve a lot of the data fetching problems. To your delete example, I agree that using the endpoints is clunky. If I'm on a product page and click a delete button that fetches product/[skuid] with an onDelete method Im going to get a 404 on the client regardless of how I've handled it in the route file.
I think named endpoints, something like
useEndpoint('/products')would solve a lot of the data fetching problems. To your delete example, I agree that using the endpoints is clunky. If I'm on a product page and click a delete button that fetchesproduct/[skuid]with anonDeletemethod Im going to get a 404 on the client regardless of how I've handled it in the route file.
That's just the thing, if you're on a product page and you click a delete button that sends a DELETE method request to products/[skuId], you will not get a 404 even if you haven't implemented onDelete. Instead, you will get a 200 OK with the same HTML as if you sent a GET request. And if you send that DELETE request as a data request with the Accept: application/json header, you will also get a 200 OK response with JSON containing isStatic and prefetch[]. As I explained all above. The correct response to receiving this request without implementing onDelete or the generic catch-all onRequest is to return a 405 Method Not Allowed with an error and with an Allow: <METHODS> header.
If you do implement the HTTP method, that DELETE fetch with accept: application/json should act as a data endpoint only and return the direct JSON data from the onDelete handler indicating status response, or however you implemented it. Not the other information regarding prefetchs, which I believe should be specifically under route + '/q-data.json' as it currently also is.
(I should mention this is on prod mode with npm run preview, most of this is still the same in dev but not all)
My experience is that you'll get a 404 on the client. Maybe I'm mis-understanding the issue, but here is a reproduction as I see it:
https://stackblitz.com/edit/qwik-starter-bgt8fn?file=src/routes/page/[slug]/index.tsx
My experience is that you'll get a 404 on the client. Maybe I'm mis-understanding the issue, but here is a reproduction as I see it:
https://stackblitz.com/edit/qwik-starter-bgt8fn?file=src/routes/page/[slug]/index.tsx
If you look at the network logs in dev tools you will clearly see that both buttons return a 404 to the client, even on the implemented handler. If you console.log the XHR response on the client side you will see there's nothing. So it's not even working right, but for other reasons. If you add accept: application/json to the request headers, you will see they both return 200 OK and extra JSON even in your stackblitz, regardless of whether it's the DELETE or POST method.
My experience is that you'll get a 404 on the client. Maybe I'm mis-understanding the issue, but here is a reproduction as I see it:
https://stackblitz.com/edit/qwik-starter-bgt8fn?file=src/routes/page/[slug]/index.tsx
As a followup, the reason the xhr requests are returning 404 on both the implemented DELETE method and unimplemented POST method is because it's in dev mode for some reason. Even GET to / returns 404 when I hit it with CURL. When you run an app in prod mode with npm run preview or one of the adapters like cloudflare, ALL requests regardless of method and whether it's implemented will in fact return 200 OK.
Also, even in dev mode, accept: application/json will still return 200 OK to any method, again with extra JSON. See example here: https://stackblitz.com/edit/qwik-starter-akmbjb?file=src/routes/page/[slug]/index.tsx
(note in prod mode there is an extra prefetch[] included along with isStatic)
Oh yeah, duh, I forgot the headers and see what you're saying. I've never noticed this issue because I setup endpoint routes.