0
  • I am trying to get auth working in my sveltekit 2.20.7 / svelte 5.27.0 application
  • I have an express backend that runs on port 8000 and uses express-session with connect-redis and passport to provide local email password authentication using HTTP only cookies
  • I have the following route structure
src
└── routes
    ├── (auth)
    │   ├── login
    │   │   ├── +page.svelte
    │   │   └── +page.ts
    │   └── signup
    │       ├── +page.svelte
    │       └── +page.ts
    ├── (news)
    │   └── [[news=newsMatcher]]
    │       └── [[tag]]
    │           ├── [id=idMatcher]
    │           │   └── [title]
    │           │       ├── +page.svelte
    │           │       └── +page.ts
    │           ├── +layout.server.ts (Call this two)
    │           ├── +layout.svelte
    │           ├── +layout.ts
    │           ├── +page.server.ts
    │           └── +page.svelte
    └── +layout.server.ts (Call this one)


  • Inside the outermost +layout.server.ts file, I am trying to fire a GET request to check if the user is currently logged in

+layout.server.ts (one)

export const load: LayoutServerLoad = async ({ fetch }) => {
    const endpoint = 'http://localhost:8000/api/v1/auth/session';
    try {
        const init: RequestInit = {
            credentials: 'include',
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json'
            },
            method: 'GET',
            signal: AbortSignal.timeout(10000)
        };
        const response = await fetch(endpoint, init);
        if (!response.ok) {
            throw new Error(`Error: something went wrong when fetching data from endpoint:${endpoint}`, {
                cause: { status: response.status, statusText: response.statusText }
            });
        }
        const user: UserResponse = await response.json();
        const { data } = user;
        return {
            user: data
        };
    } catch (e) {
        const { status, message } = handleFetchError(e, endpoint);
        error(status, message);
    }
};

  • Inside the +layout.server.ts (aka two), I merely forward the user +layout.server.ts. (two)
export const load: LayoutServerLoad = async ({ fetch, parent }) => {
    const endpoint = '...some endpoint for other data...';
    try {
        const init: RequestInit = {
            //...
        };

        const response = await fetch(endpoint, init);

        if (!response.ok) {
            throw new Error(`Error: something went wrong when fetching data from endpoint:${endpoint}`, {
                cause: { status: response.status, statusText: response.statusText }
            });
        }

        // ...process results and create required variables

        const { user } = await parent();

        return {
            // ...other variables
            user
        };
    } catch (e) {
        const { status, message } = handleFetchError(e, endpoint);
        error(status, message);
    }
};
  • This user variable now waters down further to +layout.ts +layout.ts
export const load: LayoutLoad = async ({ data, fetch, params, untrack, url }) => {
    // ...processing urls and stuff
    const user = data.user;

    // ... other variables

    return {
        // ...other variables
        user
    };
};
  • Now the way I understand layouts work, this user variable will be available everywhere throughout all the inner layout and pages, correct?

What have I tried?

  • I read in one of the threads somewhere that you can use runes to store this user
export class AuthStore {
  user = $state(null)

  login(user: {id: string, email: string, name: string} | null) {
    this.user = user
  }

  logout() {
    this.user = null
  }
}

Questions

  • How do I delete this user or set this user to null when the user does a logout?
  • Is this the right place to retrieve this session or is there a better way?
  • Where do I use that rune class above? Doesn't the documentation say dont use runes inside +layout.server.ts files?

1 Answer 1

1

As stated in your very last point, there are many serious reasons not to use states in your server files, I implemented a similar user Rune in my apps by using the context API, so the root +layout.server.ts (one) will be responsible for dealing with the server auth and pass that in the data prop to your root +layout.svelte. From there a reactive effect will update that user Rune within the context. Since the context is only available to components, you should not have issues with shared state in your server server.

The main sauce is in the +layout.svelte:

<script lang="ts">
    import { getContext, setContext } from "svelte";

    //data prop with responses from load functions
    const { data } = $props()
    let userRune = $state({})
    setContext('user', userRune)

    //this is how you'll get the synced user Rune anywhere within your app
    const userContext = getContext('user')

    $effect(() => {
    for (const property in data.user) {
            if (Object.prototype.hasOwnProperty.call(data.user, property)) {
            const element = data.user[property];
                storeContext[property] = element
        }
        }
    });

</script>

Because of certain particularities of the context API you can't reassign this context but you can update the properties within it from anywhere that can get it hence. Hence why the effect updates each property from data.user inside the userContext , but you should definitely implement your own logic (this is where you'd delete each property in the context when the user logs out). A cleaner way of doing it is by putting this whole context handling within its own file (like user.svelte.ts ) as you can see in this video from Huntabyte. A better place to retrieve the session in my opinion is your +hooks.ts files. Of course this is an extremely simplified and abstracted shape for the functionality, but it should be enough to get you going =) Hope this helps!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.