Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Official way to pass state from endpoint to fallthrough page routes #1711

Closed
Conduitry opened this issue Jun 19, 2021 · 5 comments
Closed

Official way to pass state from endpoint to fallthrough page routes #1711

Conduitry opened this issue Jun 19, 2021 · 5 comments
Labels
documentation Improvements or additions to documentation feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. router

Comments

@Conduitry
Copy link
Member

Is your feature request related to a problem? Please describe.
I'm implementing a login flow that for technical reasons needs to be an old-fashioned form submit. If the username and password that are entered fail to authenticate, I'd like the response to the POST to be the same login page, but with the username already pre-filled with what the user entered last time, and with a specific error message about what happened.

Describe the solution you'd like
I'd like the handler for a POST endpoint to be able to fall through to a page (which it already can), and for it to be able to pass some state to that page. It turns out this is already possible, by mutating request.locals inside the endpoint. Since getSession is called anew each time we try to render a request as a page, we would be able to expose whatever information we want to the page.

The question then is whether this is desired behavior. It is convenient in my case, but it could be seen as too weird or magical. So this feature request is for solidifying this as official behavior through tests and documentation.

Describe alternatives you've considered
In my case, there are other workarounds. I could manually implement this endpoint as part of the handle hook, so that I could be sure my locals values would still be present when Kit tries to render this as a page. Or on a failed login I could have the endpoint redirect (as a GET) to a URL that had the username and an error code in query parameters.

How important is this feature to you?
Being able to implement this login flow is quite important, as it's for work. The solution outlined here is somewhat important, as it seems like a nice way to solve this.

Additional context
@GrygrFlzr and I chatted briefly about this and some related issues on Discord. It's currently possible for getSession itself to mutate the request, but that seems really weird, impractical to prevent, and not something we should be worrying about defining the behavior of.

We also chatted about how routes are sorted by specificity and precedence. This is mostly described at https://kit.svelte.dev/docs#routing-advanced-fallthrough-routes but I'm wondering whether this could be made a bit more explicit. Endpoints do have higher precedence than pages, but only for routes that otherwise have the same specificity. This is a bit tricky to say in a way that is both accurate and comprehensible. We don't, for example, want to just link people to the comparator function that's used to sort them.

@benmccann
Copy link
Member

Relying on fall through here does seem a bit magical and wouldn't work if the page URL was less overlapping with the endpoint URL. Perhaps we need a way to return a page from an endpoint

@moatra
Copy link
Contributor

moatra commented Nov 27, 2021

One trick I've been trying to address this is to have the endpoint detect if the client prefers HTML over JSON (via something like the enhance action from the demo app). If the client wants html from the endpoint (ie: a non-enhanced form did a POST to the endpoint), have the endpoint on the server call fetch on localhost to the page containing the form.

This is a bit finicky -- if the endpoint is at the same path as the page it needs to have a flag on the request (eg: a fall-through-to-page header being set) so the endpoint doesn't try and handle the request again. You need to pass the error message and previous form field values in the body of the request. You also need to have a hooks implementation set up to move the body of the request into the session so that the page component can see the actual data and allow proper hydration on the client side.

It mostly works, but my main concern is having to hit the networking stack to do this. I have a couple questions here:

  1. In the docs for a page's load function, it states:

SvelteKit's load receives an implementation of fetch, which has the following special properties:

  • (...)
  • it can make requests against the app's own endpoints without issuing an HTTP call

In the docs for endpoints, it also states:

All server-side code, including endpoints, has access to fetch in case you need to request data from external APIs.

Does the former hold true for the latter? Does the fetch made available for endpoints bypass the networking stack if targeted at the running app?

  1. If not, is there any way to get access to the fully-configured middleware pipeline from endpoint code? I see hints of @sveltejs/kit/ssr, but it looks like you would need to pass it the full Options object and I don't know how to get at that (and seems silly to try and manually populate it).

  2. I thought about just trying to import the "PageWithForm.svelte" file into the endpoint code, but if it behaves like traditional svelte ssr then I expect it would just render the component, and not the full layout and/or any load function. Would it make sense to have an imported page component return a special version of the ssr logic to include the necessary layouts/hooks/session/etc? (basically bypass the router and send a request to the page component?)


The more I think about it, the more I really like option 3: Importing a page component should return the necessary logic to render the full page for a provided request, including layouts and error pages if needed. That would sidestep the need for "passing state from endpoint to fall through routes", and instead the endpoint could just say "I want run SSR for this page I imported", then shuttle the response generated via SSR out as the response for the endpoint.

@dslatkin
Copy link

It would be great if this could be addressed before a 1.0 release or at least become a p1 issue. It feels hard to say that SvelteKit can make transitional apps if there's not a built-in or sanctioned way for SvelteKit to handle server-invalidated form submissions for users without JS. I'm wondering if @Rich-Harris has seen this yet and has thoughts?

Another conversation on Discord recently led some of us to the same technique as @Conduitry - loading request.locals in an endpoint before falling through to a page, at which point getSession picks up those new locals to make them available in that page's load function. I would feel much better about it if it's clear in the docs that it's sanctioned behavior and not an abuse of locals. It also wasn't entirely clear to me that getSession may run multiple times on the same request during SSR so it would be nice if this were mentioned in the docs too?

For serving entire pages from endpoints, I personally would be very wary of addressing the issue that way. I could see routing becoming much more ambiguous by comingling which pages are served at which paths in the file tree. If a request comes in for /some-page, I would think it should be either /some-page.js or /some-page.svelte which serves the response.

@geoffrich
Copy link
Member

For those following this thread -- see #3532 for a proposed solution to render a page with new props after a POST (e.g. with validation messages).

@Rich-Harris
Copy link
Member

Closing in favour of #3532

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Improvements or additions to documentation feature / enhancement New feature or request p2-nice-to-have SvelteKit cannot be used by a small number of people, quality of life improvements, etc. router
Projects
None yet
Development

No branches or pull requests

6 participants