FullStack JWT Auth: Diving into SvelteKit - Layout and Logout
Tags: typescript, javascript, webdev, svelte
Published: 2022-02-09 11:15:12

## Intoduction

Having introduced SvelteKit and our project's structure in the previous article of this series, it's time we built something.

## Source code

The overall source code for this project can be accessed here:

{% github  Sirneij/django_svelte_jwt_auth  %}

## Live version

This project was deployed on heroku (backend) and vercel (frontend) and its live version can be accessed [here](https://django-sveltekit-jwt-auth.vercel.app/).

## Step 1: Make the layout

Since our entire app will have some uniformity in terms of navigation and footer, let's populate our routes' `__layout.svelte` with:

```javascript
<script lang="ts">
	import { notificationData } from '../store/notificationStore';
	import { fly } from 'svelte/transition';

	import Header from '../components/Header/Header.svelte';

	import '../dist/css/style.min.css';
</script>

<Header />

{#if $notificationData}
	<div class="notification-container">
		<p
			class="notification"
			in:fly={{ x: 200, duration: 500, delay: 500 }}
			out:fly={{ x: 200, duration: 500 }}
		>
			{$notificationData}
		</p>
	</div>
{/if}

<main>
	<slot />
</main>

<footer>
	<p>
		Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to learn SvelteKit. Coded by
		<a href="https://github.com/Sirneij/">John O. Idogun</a>
	</p>
</footer>

```

It's a basic structure which has `Header` component, `footer`, display of notifications, and a `slot` tag to take in other pages' contents. Auto-subscription of `notificationData` was done by appending `$` at it's beginning. `notificationData` is a writable store with the following definition in `stores/notificationStore.ts`:

```typescript
import { writable } from "svelte/store";

export const notificationData = writable("");
```

It expects a string value. `Header` is a component that houses the app's navigation and has the following content in `components/Header/Header.svelte`:

```typescript
<script lang="ts">
	import { page } from '$app/stores';
	import logo from './svelte-logo.svg';
	import john from './john.svg';
	import { userData } from '../../store/userStore';
	import { logOutUser } from '$lib/requestUtils';
</script>

<header>
	<div class="corner">
		<a href="https://kit.svelte.dev">
			<img src={logo} alt="SvelteKit" />
		</a>
	</div>

	<nav>
		<svg viewBox="0 0 2 3" aria-hidden="true">
			<path d="M0,0 L1,2 C1.5,3 1.5,3 2,3 L2,0 Z" />
		</svg>
		<ul>
			<li class:active={$page.url.pathname === '/'}>
				<a sveltekit:prefetch href="/">Home</a>
			</li>
			{#if !$userData.username}
				<li class:active={$page.url.pathname === '/accounts/login'}>
					<a sveltekit:prefetch href="/accounts/login">Login</a>
				</li>
				<li class:active={$page.url.pathname === '/accounts/register'}>
					<a sveltekit:prefetch href="/accounts/register">Register</a>
				</li>
			{:else}
				<li>
					Welcome, <a sveltekit:prefetch href="/accounts/user/">{$userData.username}</a>
				</li>
				<li>
					<a href={null} on:click={logOutUser} style="cursor: pointer;">Logout</a>
				</li>
			{/if}
		</ul>
		<svg viewBox="0 0 2 3" aria-hidden="true">
			<path d="M0,0 L0,3 C0.5,3 0.5,3 1,2 L2,0 Z" />
		</svg>
	</nav>

	<div class="corner">
		<a href="https://github.com/Sirneij/">
			<img src={john} alt="John O. Idogun" />
		</a>
	</div>
</header>
```

This component introduces a couple important imports:

- `page`: To keep track of the current page, we imported the built-in [page][1] and utilizing its `url` object, we dynamically added `active` classes to the navigation items. `page` store contains an object with the current `url`, `params`, `stuff`, `status` and `error`.

- `logo` and `john` are just images which are in the same directory as the `Header.svelte` file.

- `userData`: Just like `notificationData`, `userData` is a custom writable store exported from `stores/userStore.ts` to make available current user's data. It has the following definition:

  ```typescript
  import { writable } from "svelte/store";

  export const userData = writable({});
  ```

  These data are updated/set during login and logout operations.

- `logOutUser` is one of the many functions domiciled in the `lib/requestUtils.ts` file. It's purpose is to log the current user out and subsequently reset the `userData` to an empty object. The implementation is shown below:

  ```typescript
  //lib -> requestUtils.ts
  ...
  export const logOutUser = async () => {
  	const res = await fetch(`${BASE_API_URI}/token/refresh/`, {
  		method: 'POST',
  		mode: 'cors',
  		headers: {
  			'Content-Type': 'application/json'
  		},
  		body: JSON.stringify({
  			refresh: `${browserGet('refreshToken')}`
  		})
  	});
  	const accessRefresh = await res.json();
  	const jres = await fetch(`${BASE_API_URI}/logout/`, {
  		method: 'POST',
  		mode: 'cors',
  		headers: {
  			Authorization: `Bearer ${accessRefresh.access}`,
  			'Content-Type': 'application/json'
  		},
  		body: JSON.stringify({
  			refresh: `${browserGet('refreshToken')}`
  		})
  	});
  	if (jres.status !== 204) {
  		const data = await jres.json();
  		const error = data.user.error[0];
  		throw { id: error.id, message: error };
  	}
  	localStorage.removeItem('refreshToken');
  	userData.set({});
  	notificationData.set('You have successfully logged out.')
  	await goto('/accounts/login');
  };
  ```

  From the snippet, we made the first POST request to `BASE_API_URI//token/refresh/` sending the current user's `refresh` token. This request returns the user's `access` token which was used as `Authorization` header for the `/logout/` endpoint. This process is required as only authenticated users can logout. If the response is successful, we remove `refreshToken` from the localStorage, reset `userData`, set `notificationData` to something informative, and then redirect the user to `accounts/login` page. That's basically it! Some notable helper functions are the `browserSet` and `browserGet` which help set/save and get from the localStorage. Their implementations ain't hard to decipher:

  ```typescript
  import { browser } from '$app/env';
  ...

  export const browserGet = (key: string):string | undefined => {
  	if (browser) {
  		const item = localStorage.getItem(key);
  		if (item) {
  			return item;
  		}
  	}
  	return null;
  };

  export const browserSet = (key:string, value:string) : void => {
    if (browser) {
        localStorage.setItem(key, value);
    }
  };
  ```

  We utilized the built-in `browser` to ensure we are in the browser environment before setting and getting items from the localStorage.

That is it for this part. Up next is how we handled registrations and user logins. Stay with me...

## Outro

Enjoyed this article, consider [contacting me for a job, something worthwhile or buying a coffee ☕](mailto:sirneij@gmail.com). You can also connect with/follow me on [LinkedIn](https://www.linkedin.com/in/idogun-john-nelson/).

[1]: https://kit.svelte.dev/docs#modules-$app-stores "App stores"