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

Writable page store #6181

Closed
fcrozatier opened this issue Aug 23, 2022 · 14 comments
Closed

Writable page store #6181

fcrozatier opened this issue Aug 23, 2022 · 14 comments

Comments

@fcrozatier
Copy link
Contributor

fcrozatier commented Aug 23, 2022

Describe the problem

The session store was removed in #5946 and the suggested pattern instead is to use the page store to access the data fetched in the main layout #5883

But the session store was writable, where the page store is only readable

So let say I do not fecth all the user data in my main layout, but then need to update it or add data to it from a +page. How could I do this?

Describe the proposed solution

I don't see all the implications of this solution but a writable page store would allow this pattern.

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

@CodexAnima
Copy link

@fcrozatier That was my problem, too. I have a popup login and before API change I could update session without to reload/refresh the page and was good to go.

Now I POST fetch my login endpoint (+server.js, export const POST = async ({ locals, params, request })) and await my api response. When the response is ok, I do locals.user = response.user; and return a new Response back to the Login.svelte.

In Login.svelte (import { invalidate } from "$app/navigation";) then on the positive Response from the endpoint I do invalidate(). This triggers my root +layout.server.js which returns { user: locals.user }.

That updates the page store behind and updates the $page.data.user without reloading/refreshing my current page.

Maybe this might be a way for your solution :)

@fcrozatier
Copy link
Contributor Author

I've tried to implement your solution but when I update the locals in the endpoint, even if I invalidate afterwards, it does not reflect in the main layout locals.

Here is a repo of what I tried

@CodexAnima
Copy link

CodexAnima commented Aug 23, 2022

@fcrozatier I am using plain javascript and and the static adapter. Common data is stored in a writeable storage, user data is stored inside an https-only cookie. Let me see....

routes:

  • /api/login/+server.js
  • /+layout.svelte
  • /+layout.server.js
  • /+page.svelte

+page.svelte

import { invalidate } from "$app/navigation";
let userdata = [INPUT FROM LOGIN FORM];
const query = await fetch( "/api/login/", {
    method: 'POST',
    headers: {
        "Accept": "application/json",
        "Content-Type": "application/json",
    },
    body: userdata && JSON.stringify(userdata)
});

const fromEndpoint = await query.json();

if ( query.ok ) {
    invalidate();
    document.querySelector( ".close" ).click(); //CLOSING LOGIN POPUP
}
else throw new Error( 'Some error occurred.' );

+layout.svelte
(import page...)
showing $page.data.user here

+layout.server.js

import cookie from 'cookie';  
export const load = async ({ locals, request, setHeaders }) => {  
    const cookies = cookie.parse( request.headers.get("cookie") || "" );  
    
    locals.user = cookies.user ?? crypto.randomUUID();  
    
    return {  
        user: locals.user  
    };  
};

+server.js

export const POST = async ({ locals, params, request }) => {
    const data = await request.json();
    const response = await api({ action: 'login', ...data });

    if ( response.message && response.message === "ok" ) {
        locals.user = response.user;
        return new Response( JSON.stringify({
            user: response.user,
            message: response.message
        }), {
            headers: {
                "Set-Cookie" : cookie.serialize( "user", response.user, {
                    path: "/", httpOnly: true, sameSite: "strict", secure: true
                })
            }
        });
    }
    else throw error( 503, 'You shall not pass.');
};

@stalkerg
Copy link
Contributor

I found this issue too. Most importantly, the invalidation of the session request forced the client to ask the server again about the session but didn't update the $page at all. As I remember, @Rich-Harris advised to use reset invalidation for load function in +layout.server.js, but it's not working.

@stalkerg
Copy link
Contributor

Basically, without such a feature, I can't update the user parameters like name, avatar and etc.

@CodexAnima
Copy link

CodexAnima commented Aug 23, 2022

@stalkerg My problem here is, the login form is not a separate page that can reload/refresh but a popup login. So the current page is not triggering any page.(server.)js files. With the invalidation from the endpoint response it is working. Maybe my setup with static adapter is doing some magic that worked before SvelteKit API change and now works again. Yet :D

@stalkerg
Copy link
Contributor

@CodexAnima ok, just invalidation() is working, invalidation('you_api_for_get_session') not working but with getting a new session.
Hmmm

@fcrozatier
Copy link
Contributor Author

@CodexAnima in your case I think it's the cookie that makes it work. Remove the cookie and then I guess you get no chocolate.

headers: {
"Set-Cookie" : cookie.serialize( "user", response.user, {
path: "/", httpOnly: true, sameSite: "strict", secure: true
})
}

That would be convoluted to have to write a cookie each time I want to communicate data to the root layout, there must be another way ?

@CodexAnima
Copy link

CodexAnima commented Aug 23, 2022

@fcrozatier well, the cookie keeps the logged in user persistent even when reloading the page. logging out removes the cookie and user is back to default crypto.randomUUI(). Inside the cookie is a token stored for querying the api and get current users relevant data on +page.svelte (over the +page.server.js). So I need that cookie (even before SvelteKit API change).

@fcrozatier
Copy link
Contributor Author

@CodexAnima Ok I see your point, using cookies is a perfectly valid design and maybe it's the right way to achieve communication to the root layout.

@CodexAnima
Copy link

@fcrozatier then again, keep in mind I need this setup for a full static adapter build for persisting user logged status.
If you use a node.js/dynamic background, you may have more options.

Since then I keep my cookies and chocolate ;)

@fcrozatier
Copy link
Contributor Author

Ok so to summarize the discussion:

  • If you need to update or add data it's means there is a user action
  • You can capture this action with a form to trigger an action endpoint
  • You can then create or update cookies which means data will persist, will be availabe in both the server and the client, plus it will be in sync

In the end this design may be superior to a writable page store were data won't persist, won't be available in the server and thus won't be in sync :)

@CodexAnima
Copy link

Maybe there will be a similar implementation in a future SvelteKit version before version 1.0 🙏

@fcrozatier
Copy link
Contributor Author

Yep, seems to be where the project is heading #5875

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants