qwik: [๐Ÿž] sharedMap not available in server$ function

Which component is affected?

Qwik City (routing)

Describe the bug

I have a layout middleware that loads the current session to sharedMap, like this:

export const onRequest: RequestHandler = async ({ sharedMap }) => {
  sharedMap.set("session", session);
};

Then I have a server$ function in a nested route that tries to access shared map but it returns an empty object, the function is like this:

export const getOffers = server$(async function () {
  const { sharedMap } = this;
  console.log("SHARED MAP", sharedMap);
});

Reproduction

https://stackblitz.com/edit/qwik-starter-gta4fs?file=src/routes/index.tsx

Steps to reproduce

As server functions donโ€™t work in stackblitz, youโ€™ll have to download the code and execute it locally

System Info

System:
    OS: Linux 6.2 Fedora Linux 38 (Workstation Edition)
    CPU: (12) x64 Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz
    Memory: 18.60 GB / 31.19 GB
    Container: Yes
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 18.16.0 - ~/.volta/tools/image/node/18.16.0/bin/node
    npm: 9.5.1 - ~/.volta/tools/image/node/18.16.0/bin/npm
  Browsers:
    Chrome: 112.0.5615.165
    Firefox: 112.0.1
  npmPackages:
    @builder.io/qwik: ^0.100.0 => 0.100.0 
    @builder.io/qwik-city: ^0.100.0 => 0.100.0 
    undici: 5.21.0 => 5.21.0 
    vite: 4.2.1 => 4.2.1

Additional Information

No response

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 18 (13 by maintainers)

Most upvoted comments

Any progress or ideas on this @manucorporat?

@juanpmarin Yes I understand, server$ has a simpler API and is more flexible, whereas ...Action$ is more concrete and designed around forms/actions. I donโ€™t think there is currently a pleasing and simple way to achieve the following:

  1. Populate some piece of data on the page during SSR.
  2. Call back to the server to re-fetch data based on some criteria. (periodic, etc)
  3. Have this be protected by a route guard. (layouts)
  4. Do this in a performant and precise manner. (without re-fetching all other loaders, etc.)

I feel like there should be a streamlined way to achieve this like with a single routeLoader$ for example. It is achievable using a combination of features but with the following issues:

  1. ...Loader$s only load once on their own, no way to programmatically refresh only a specific loader without re-running or re-loading data for all the others.
  2. ...Action$s can return data but cause all ...Loader$s to reload.
  3. ...Action$s returning data cannot be called during SSR. (inside useTask$ for example)
  4. server$ works but is not route guarded and appears not intended to be.

With that said, this issue may be relevant to the above problem: https://github.com/BuilderIO/qwik/issues/2984

@manucorporat Nice, didnโ€™t know about that! Works half-way, errors with TypeError: Cannot read properties of undefined (reading 'sharedMap') in SSR context, like calling server$ inside a useTask$:

export const getSession = server$(async function() {
  console.log("Calling from `server$`...");
  return this.sharedMap.get("session") as string;
});

export default component$(() => {
  const sessionKey = useSignal<string>();

  useTask$(async () => {
    // This will error during SSR as `sharedMap` is `undefined`.
    sessionKey.value = await getSession();
  });

  return (
    <>
      <div>Session key: {sessionKey.value}</div>
      <button onClick$={async () => sessionKey.value = await getSession()}>Get Key!</button>
    </>
  );
});