Sapper: Svelte 3 equivalent of Sapper's store

Created on 30 Jan 2019  路  2Comments  路  Source: sveltejs/sapper

Currently, with Svelte 2, you can pass data from server to client like so:

// server.js
app.use(sapper.middleware({
  store: req => ({
    user: req.session && req.session.user
  })
})
// client.js
sapper.start({
  target,
  store: data => new Store(data)
})

Behind the scenes, Sapper handles [de]serialization.

This is the accepted way to handle problems related to authentication, because user data has a couple of important characteristics:

  • You really don't want to accidentally leak it between two sessions on the same server, and generating the store on a per-request basis makes that very unlikely
  • It's often used in lots of different places in your app, so a global store makes sense.

But Svelte 3 doesn't have global stores that are passed around in quite the same way. One option would be to generate props, and rely on the developer to pass them around as necessary (including via layout components). This would be cumbersome, and would encourage developers to populate stores from inside components, which makes accidental data leakage significantly more likely.

A second option is to create the store on the generated app:

// generated code
import { writable } from 'svelte/store';
export const store = writable(__SAPPER__.store); // serialized and included on the page
// anywhere inside your app
<script>
  import { store } as app from '@sapper/app'; // see #551
</script>

{#if $store.user}
  <h1>Hello {$store.user.name}!</h1>
{:else}
  <p>please <a href="login">log in</a></p>
{/if}

(See #551 for an explanation of @sapper/app.)

Does that make sense? Is store the right name? (session?)

Most helpful comment

Just realised this doesn't actually work. If store is just something exported by the app, there's no way to prevent leakage.

Instead, it needs to be tied to rendering, which means we need to use the context API. Sapper needs to provide a top level component that sets the store as context for the rest of the app. You would therefore only be able to access it during initialisation, which means you couldn't do it inside a setTimeout and get someone else's session by accident:

<script>
  import * as app from '@sapper/app';

  // this works...
  const store = app.session();

  // but this would fail
  setTimeout(() => {
    const store = app.session();
  });
</script>

(app.session uses getContext under the hood.)

All 2 comments

Just realised this doesn't actually work. If store is just something exported by the app, there's no way to prevent leakage.

Instead, it needs to be tied to rendering, which means we need to use the context API. Sapper needs to provide a top level component that sets the store as context for the rest of the app. You would therefore only be able to access it during initialisation, which means you couldn't do it inside a setTimeout and get someone else's session by accident:

<script>
  import * as app from '@sapper/app';

  // this works...
  const store = app.session();

  // but this would fail
  setTimeout(() => {
    const store = app.session();
  });
</script>

(app.session uses getContext under the hood.)

Implemented on current master, alongside #554 which makes it much harder to accidentally keep logged-in state visible after a client-side logout

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rich-Harris picture Rich-Harris  路  4Comments

matt3224 picture matt3224  路  4Comments

keyvan-m-sadeghi picture keyvan-m-sadeghi  路  4Comments

freedmand picture freedmand  路  4Comments

Rich-Harris picture Rich-Harris  路  3Comments