Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write on session with Sapper and Svelte

I wrote a Sapper app with session management following the RealWorld example:

polka()
  .use(bodyParser.json())
  .use(session({
    name: 'kidways-app',
    secret: 'conduit',
    resave: false,
    saveUninitialized: true,
    cookie: {
      maxAge: 31536000
    },
    store: new FileStore({
      path: 'data/sessions',
    })
  }))
  .use(
    compression({ threshold: 0 }),
    sirv('static', { dev }),
    pdfMiddleware,
    sapper.middleware({
      session: req => ({
        token: req.session && req.session.token
      })
    })
  )
  .listen(PORT, err => {
    if (err) console.log('error', err);
  });

Then on my _layout.sevlte:

<script context="module">
  export async function preload({ query }, session) {
    console.log('preload', session)
    return {
      // ...
    };
  }
</script>

<script>
  import { onMount, createEventDispatcher } from 'svelte';
  import { Splash } from 'project-components';
  import * as sapper from '@sapper/app';
  import { user } from '../stores';
  import client from '../feathers';

  const { session } = sapper.stores();

  onMount(async () => {
    try {
      await client.reAuthenticate();
      const auth = await client.get('authentication');
      user.set(auth.user);
      $session.token = 'test';
    } catch (e) {
    } finally {
      loaded = true;
    }
  });

  console.log($session)
</script>

<h1>{$session.token}</h1>

This work on client side rendering, but the token is still undefined on preload, making my SSR template rendering broken.

What did I missed?

like image 357
Soullivaneuh Avatar asked Feb 22 '20 19:02

Soullivaneuh


People also ask

What is svelte and Sapper?

Sapper is the companion component framework to Svelte that helps you build larger and more complex apps in a fast and efficient way. In this modern age, building a web app is a fairly complex endeavor, with code splitting, data management, performance optimizations, etc.

How to maintain user sessions in Sapper?

If you are using Sapper as an authentication/authorization server, you can use session middleware such as express-session in your app/server.js in order to maintain user sessions. If the user navigated to /blog/some-invalid-slug, we would want to render a 404 Not Found page. We can do that with this.error:

Is there an idiomatic svelte / Sapper way to guarantee code runs client-side?

Is there an idiomatic Svelte / Sapper way to guarantee code only runs client-side, when it has access to cookies? way to guarantee code only runs client-side JS files send from server are runned at client side, cookies are send to server with request - this is all you need. In Svelte / Sapper, the code also runs (and fails) server-side.

Does session return a promise in Sapper?

The session function may return a Promise (or, equivalently, be async ). Note that if session returns a Promise (or is async ), it will be re-awaited for on every server-rendered page route. Sapper uses code splitting to break your app into small chunks (one per route), ensuring fast startup times.


1 Answers

When a page renders, session is populated according to the return value of the function you specified here:

sapper.middleware({
  session: req => ({
    token: req.session && req.session.token
  })
})

So while the client may have an up-to-date token, it won't take effect on page reload unless you somehow persist the token to the server in such a way that the session middleware knows about it.

Typically you'd achieve this by having a server route, like routes/auth/token.js or something...

export function post(req, res) {
  req.session.token = req.body.token;

  res.writeHead(200, {
    'Content-Type': 'application/json'
  });

  res.end();
}

...and posting the token from the client:

onMount(async () => {
  try {
    await client.reAuthenticate();
    const auth = await client.get('authentication');
    user.set(auth.user);

    await fetch(`auth/token`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ token })
    });

    // writing to the session store on the client means
    // it's immediately available to the rest of the app,
    // without needing to reload the page
    $session.token = 'test';
  } catch (e) {
  } finally {
    loaded = true;
  }
});
like image 158
Rich Harris Avatar answered Sep 21 '22 22:09

Rich Harris