My website is at large a static site, but I would still like auth and session functionality. I imagine there's a way to statically generate the site, but then override something like you're not logged in message to logged in as "user" on hydration right? More importantly would it work with prefetching?
This part of the docs is confusing:
In other words, any app that involves user sessions or authentication is not a candidate for sapper export.
The really important rule is this one:
The basic rule is this: for an app to be exportable, any two users hitting the same page of your app must get the same content from the server.
Which does not preclude you from having some code, the same for all users, but that behaves differently based on client-side sessions (e.g. localStorage...) or whatever you can normally do on the client side.
For example, you could have code like this anywhere in your app:
<script>
const isServer = typeof window === 'undefined'
const getUserInfo = async page => {
if (isServer) {
// on server, returns a promise that never resolves, to always
// render as "loading" (adjust to your needs)
return new Promise()
}
// on client, resolve from localStorage, HTTP request, or whatever
return loadUser(page)
}
</script>
{#await getUserInfo()}
<div>Loading...</div>
{:then userInfo}
{#if userInfo && userInfo.userId != null}
<div>Logged in as {userInfo.username}</div>
{:else}
<a href="/login">Log in</a>
{/if}
{:catch err}
<div>Oops: {err}</div>
{/await}
Obviously, this wouldn't get prefetched (Sapper does not analyze what your code does).
However, you can also resolve user info from preload
, with something like this:
<script context="module">
import { loadUser } from './api'
const isServer = typeof window === 'undefined'
export const preload = async page => {
// if preload returns nothing on the server, then it will be called
// again in the client (otherwise the client will be passed the preloaded
// object and preload could be skipped in the client -- see bellow)
if (isServer) return null
const { data } = await loadUser(page)
return { user: data }
}
</script>
<script>
// our user from preload
export let user
</script>
{#if user && !user.anonymous}
<div>Hello, {user.name}</div>
{:else}
<a href="/login">Log in</a>
{/if}
This will have the effect of (1) deferring the rendering of the page until the promise returned by preload
is resolved, and (2) starting to resolve said user info when the page is prefetched.
If you intend to use preload
to resolve dynamic content, it is important that you return nothing (undefined
or null
) from it in the server. Otherwise, if the page is accessed directly by URL (for example, if you reload the same page), then the server data will be passed on to the client, and preload
won't be called client-side. Very subtle bug. Avoid it.
So, to sum it up, anything that is returned by preload
in the server will be included in your static site (hence, publicly accessible). But you can still manage sessions and load dynamic content in the client. If you do so from a preload
function, then this loading will be prefetched as usual.