How to build a full stack serverless application with Svelte and GraphQL
Tags: svelte, serverless, fauna, javascript
Published: 2022-01-26 12:56:53

In this tutorial, you learn to will build a full-stack serverless application with [Svelte.js](https://svelte.dev/), GraphQL, and Fauna. You will build a blogging platform similar to [Dev.to](https://dev.to/), [hashnode.com](https://hashnode.com/), or [Medium](https://medium.com/). Users will be able to sign in to your application, create new posts, edit, and delete their own posts.

We will be using the following technology stack.

- Svelte.js ([Sveltekit](https://kit.svelte.dev/))
- GraphQL
- Fauna for the database
- Deployment (Vercel or Netlify)

🤖 You can find the final code in the following [github](https://github.com/Shadid12/svelte-fauna-blog??utm_source=Devto&utm_medium=referral&utm_campaign=shadid_svelte_graphql) link.

## Create a new Svelte App

First, go ahead and create a new Svelte app. Run the following commands in our terminal.

```bash
npm init svelte@next blogApp
```

The Svelte CLI will give you some options to customize our application. Choose the following options.

`✔ Which Svelte app template? › Skeleton project`

`✔ Use TypeScript? … No`

`✔ Add ESLint for code linting?  Yes`

`✔ Add Prettier for code formatting? Yes`

Run our newly created application with the following command.

```bash
cd blogApp
npm i
npm run dev
```

![Preview of running sample app](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0odw1dlh109u2s1nbsuc.png)

In this tutorial, we will primarily focus on the functionality of our application. We will not spend too much time styling. Let’s go ahead and create a simple Navbar component. Create a new file `src/lib/Nav.svelte` and add the following code in there.

```jsx
// src/lib/Nav.svelte

<nav>
  <a href="/">Home</a>
  <a href="/login">Login</a>
  <a href="/register">Register</a>
</nav>
```

Next, let’s create a layout file. Create a new file `src/routes/__layout.svelte` and add the following code. 

```jsx
// src/routes/__layout.svelte
<script>
    import Nav from '$lib/Nav.svelte';
  </script>

<Nav />
<slot></slot>
```

Now when you run the application, a `Navbar` component will appear in each page. 


![App with navbar component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qq19s21fj71bgyv32szq.png)

## Setting up Svelte GraphQL client

Your Svelte app will be consuming a GraphQL backend service. There are many popular libraries that you can use to consume GraphQL in Svelte. The **`@urql/svelte`** library is one of the most popular ones. Let’s go ahead and set it up.

Run the following command to add the library in your project.

```bash
npm i @urql/svelte --save
```

Next create a new file `src/client.js` and add the following code snippet.

```jsx
// src/client.js

import { createClient } from '@urql/svelte';

export default createClient({
  url: 'https://graphql.us.fauna.com/graphql',
  
  // For DB in other zone use the following url
	// EU: https://graphql.eu.fauna.com/graphql
  // Classic: https://graphql.fauna.com/graphql

  fetchOptions: () => {
    const token = import.meta.env.VITE_PUBLIC_FAUNA_KEY;
    return {
      headers: { authorization: token ? `Bearer ${token}` : '' },
    };
  },
}); 
```

We are now ready to query data from GraphQL backend. Let’s go ahead and setup our database now.

## Setting up the Database

Go ahead and create a new account with [Fauna](https://dashboard.fauna.com/?utm_source=Devto&utm_medium=referral&utm_campaign=shadid_svelte_graphql) if you haven’t done already. Fauna is a distributed serverless database that uses native GraphQL API. 

Head over to the Fauna dashboard and create a new database.

![Fauna create new database](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1bcdekls098w8ggd90le.png)
 
You are now ready to define our GraphQL schema. The following ULM diagram describes how to model the data in your application. In this app you have users and every user can have many posts. It is a `has_many` relationship between `User` and `Post`.

![Database relationship](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qafryv57rdnneubchb95.png)
 
Head back over to the code and create a new file `schema.graphql` in your root directory. Add the following code.

```graphql
# schema.graphql

type User {
  username: String!
  email: String!
  posts: [Post!] @relation
}

type Post {
  title: String!
  content: String!
  author: User!
}

type Query {
  listPosts: [Post]
}
```

Next, upload the schema to your Fauna database. Head over to Fauna dashboard, select GraphQL and import schema. Import the `schema.graphql` file.

![Upload schema to Fauna](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nlm2qlarrkzzyaf5vhhy.png)

Notice that you are presented with a GraphQL playground once the scheme is uploaded. You can add, modify and debug your GraphQL api from this playground. 

![Graphql playground overview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4m6rx7hk996z17fzq2e4.png)

Let’s go ahead and add some data to our database. Create a new user by running the following mutation inside the GraphQL playground.

```graphql
mutation CreateNewUser {
  createUser(data: {
    username: "shadid"
    email: "shadid120@email.com"
  }) {
    _id
    username
    email
  }
}
```

![Mutation CreateNewUser running on graphql playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2h0sbdxj4an3m5hp1o8i.png)
 
Similarly, create a new post. Run the following mutation in the GraphQL playground to create a new post. 

```graphql
mutation CreatePost {
  createPost(data: {
    title: "Hello worlds"
    content: "Some content"
    author: {
      **connect: "321522241336508481"**
    }
  }) {
    _id
    title
    content
    author {
      email
    }
  }
}
```

Notice that we used the author > connect field. You add the userId from the previous mutation here. This will associate the user with the post. Therefore this post’s author will be the user you created in the first mutation.

![Create post mutation running on graphql playground](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stvg6h4ei6h85xwwnhfn.png)
 
## Querying data from Svelte App

Let’s go ahead and query the data from our Svelte application. We first need to specify a role and generate a key for our frontend to communicate with the database. 

Head over to Fauna dashboard. Select *Security > Roles > New Custom Role.*

![Creating a new custom role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6olbj06g00viu4kdysr3.png)
 
Give your role a name and provide read access to `User` and `Post` collections. Also provide read access to `post_author_by_user` index and `listPosts` index.

![Giving permission to custom role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vmd8pe3oc3v3khqp01is.png)

Now navigate to *Security* > *Keys* > *New Key.* 

![Creating new key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0aucqlefrou3gqwbceba.png)
 
Create a new key for your `SvelteApp` role.
 
![Associate key with role](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3kl6use5kxecslxirjhl.png)
 
![Generated new key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51ym2xjan1ys4ndluuh5.png)

Next, copy the generated key. Create a new file `.env` in the root of your application and add the key as an environment variable. 

```bash
# .env
VITE_PUBLIC_FAUNA_KEY=<Your Key Here>
```

Notice that this key is a public key and it will be exposed to the front end. This is why the role associated with this key only has read access.  

Now on the home page let’s pull in all the posts from your database. Add the following code to your `src/routes/index.js` file.

```jsx
<script lang="js">
	import { operationStore, query, setClient} from '@urql/svelte';
	import client from '../client'
	setClient(client);

  const allPosts = operationStore(`
    query GetAllPosts($size: Int!, $cursor: String) {
      listPosts(_size: $size, _cursor: $cursor) {
        data {
          _id
          title
          author {
            email
          }
        }
      }
    }
  `,
  { size: 100 },
  { requestPolicy: 'network-only' }
  );

	query(allPosts);
  
</script>

<h1>Posts</h1>

{#if $allPosts.fetching}
<p>Loading...</p>
{:else if $allPosts.error}
<p>Oh no... {$allPosts.error.message}</p>
{:else}

{#each $allPosts.data.listPosts.data as post}

<div class="post-wrap">
  <a href={`/posts/${post._id}`}>
    <div>{post.title}</div>
  </a>
  <span>by {post.author.email}</span>
</div>

{/each}

{/if}

<style>
  .post-wrap {
    margin-bottom: 1rem;
  }
</style>
```

Restart your application. Notice that you are now getting all the posts in the root URL of your app.

![App showing all posts on index route](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sx3zrqodlnvfe8lmdq4r.png)

Notice that when you select a post the app takes you to `/post/:id` route. You will be able to see individual posts in this route. Let’s go ahead and create this route. 

Create a new file `routes/posts/[id].svelte` and add the following code.

```jsx
// routes/posts/[id].svelte

<script lang="js">
  import { operationStore, query, setClient} from '@urql/svelte';
  import { page } from '$app/stores';
	import client from '../../client'
	setClient(client);

  const currentPost = operationStore(`
    query GetPostById($id: ID!) {
      findPostByID(id: $id) {
        _id
        title
        content
        author {
          email
        }
      }
    }
  `,
  { id: $page.params.id }
  )

  query(currentPost)

</script>

{#if $currentPost.fetching}
<p>Loading...</p>
{:else}

<h2>{$currentPost.data.findPostByID.title}</h2>
<p>By <b>{currentPost.data.findPostByID.author.email}</b></p>
<p>{$currentPost.data.findPostByID.content}</p>
{/if}
```

## Authentication and Authorization

Next, let’s go ahead and add authentication to our application. We can easily add authentication using the `fauna-gql-upload` and `fauna-graphql-tool` library. First go let’s go ahead and add these dependencies to our project.

```bash
npm i @fauna-labs/graphql-tool fauna-gql-upload --save-dev
```

These libraries are automation scripts and you need an admin key from Fauna to run these tools.

Head over to the Fauna dashboard. 

Select Security > Keys > New Key.

![Creating new key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwhs44krfnu8fu40fc1j.png)

Create a new Admin key. Make sure the role is set as admin.


![Admin key](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k9g2dlte6etwkl8es2b0.png)
 

**_Do not share this admin key with anyone or deploy it with your application. Admin key should only be used with automation/migration tools._**
 

Add the admin key to `.env` variable. Make sure that your `.env` file is in the gitignore list.

```bash
##.env
VITE_PUBLIC_FAUNA_KEY=<Fauna Public Key>
FGU_SECRET=<Your Admin Key>

```

Next, you have to make the following changes to your GraphQL schema.

```bash
type User **@auth(primary: "email")** {
  username: String!
  email: String!
  posts: [Post!] @relation
}

type Post **@protected(membership: "User", rule: ["read", "write", "create"])** {
  title: String!
  content: String!
  author: User!
}

type Query {
  listPosts: [Post]
}
```

Notice in the previous code block we added a `@auth` directive to our User collection. This means that we will be using the User collection to authenticate. The `primary` key defines which fields will be used to register and login users. In this case, it is `email`. Therefore users can login using their email and password.

Notice that there is an **`@protected`** directive added to Post collection**.** This directive defines the access patterns. Logged in users are allowed to write, create new posts.

Once you add these changes to your schema open up the `package.json` file and add the following code snippet in the script section.

```bash
// package.json

{
 ...
 "script": {
   ...
   "fgu": "fgu",
   "fgt": "fgt"
 }
}
```

We are adding these scripts here so we can run `fauna-graphql-tool` (fgt) and `fauna-gql-upload` (fgu) from npm. 

`fgt` takes your GraphQL schema and compiles the schema into various database resources (i.e. Collection, User Defined Functions, Authentication Rules) and `fgu` uploads the resources to Fauna.

Finally, run the following command in your terminal

```bash
npm run fgt && npm run fgu
```

Notice that a new folder name`/fauna` is generated with all the resources.

![Generated files](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hdajk650b8832s6yfi0j.png)

- 📗 Pro Tip:
    
    Notice, that a new folder called `/fauna` is created when you run the scripts. You can open this folder up and observe the various functions and roles the automation scripts has created. If you wan to customize your authentication rules further feel free to change thelogic here.
    

If you are interested in how these resources work then go over the documentation on [Fauna JavaScript drivers.](https://docs.fauna.com/fauna/current/drivers/javascript?lang=javascript?utm_source=Devto&utm_medium=referral&utm_campaign=shadid_svelte_graphql)

Now, when you go back to GraphQL playground in Fauna, you will notice that `register` and `login` mutation are available for you.

![showing mutations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12egsby3xpxug1di9fua.png)
 
Finally, head over to Security > Roles > SvelteRole and give your role call privilege to these newly generated functions. Make sure to give read access to `user_by_email` index as well since this index is used by the login function.

![configuring permissions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fj9wzzv1u46tqztvsnhd.png)

### User Registration Form

Next, let’s go ahead and create the user registration form. Create a new file `src/routes/register.svelte` and add the following code.

```jsx
// src/routes/register.svelte

<script lang="js">
  import { setClient, mutation } from '@urql/svelte';
  import client from '../client'
  import { goto } from '$app/navigation';

  setClient(client);

  const registerMutation = mutation({
    query: `
      mutation ($email: String!, $password: String!) {
        register(email: $email, password: $password) {
          email
          _id
        }
      }
    `,
  });

  async function onSubmit(e) {
    const formData = new FormData(e.target);

    const data = {};
    for (let field of formData) {
      const [key, value] = field;
      data[key] = value;
    }
    const { email, password } = data;
    const resp = await registerMutation({ email, password })
    if (resp.data.register) {
      goto('/');
    } 
    if(resp.error) {
      alert(resp.error.message);
      console.log(resp.error);
    }
  }
</script>

<div class="wrap">
  <h3>Register New User</h3>
  <form on:submit|preventDefault={onSubmit}>
    <div>
        <label for="name">Email</label>
        <input
          type="text"
          id="email"
          name="email"
          value=""
        />
    </div>
    <div>
      <label for="name">Password</label>
      <input
        type="password"
        id="password"
        name="password"
        value=""
      />
    </div>
    <button class="button is-light" type="submit">Register</button>
  </form>
</div>
```

In the previous code block you have a simple form component. On form submit the `register` mutation runs and a new user is registered.

### User Login form

Next, let’s go ahead and create a user login form. We can save the user session in the browser cookies. The `js-cookie` library let us do this easily. Add this library by running the following command in your terminal.

```bash
npm i js-cookie --save
```

Create a new file `src/routes/login.svelte` and add the following code. 

```jsx
<script>
  import { setClient, mutation } from '@urql/svelte';
  import client from '../client';
  import Cookies from 'js-cookie';
  import { goto } from '$app/navigation';

  setClient(client);

  const loginMutation = mutation({
    query: `
      mutation ($email: String!, $password: String!) {
        login(email: $email, password: $password) {
          secret
          ttl
          data {
            _id
            email
          }
        }
      }
    `,
  });
  async function onSubmit(e) {
    const formData = new FormData(e.target);

    const data = {};
    for (let field of formData) {
      const [key, value] = field;
      data[key] = value;
    }
    const { email, password } = data;
    const resp = await loginMutation({ email, password })
    
    if(resp.data.login.data) {
      Cookies.set(
        'MY_BLOG_APP_TOKEN', 
        JSON.stringify({
          id: resp.data.login.data._id,
          secret: resp.data.login.secret
        }), 
        { expires: resp.data.login.data.ttl }
      );
      alert('Login Successful');
      goto('/')
    }
  }
</script>

<div>
  <h3>Login Form</h3>
  <form on:submit|preventDefault={onSubmit} >
    <div>
      <label for="name">Email</label>
      <input
        type="text"
        id="email"
        name="email"
        value=""
      />
    </div>
    <div>
      <label for="name">Password</label>
      <input
        type="password"
        id="password"
        name="password"
        value=""
      />
    </div>
    <button type="submit">Submit</button>
  </form>
</div>
```

In the previous code block, you have a simple form component. On form submit the `login` mutation is triggered. On successful login, Fauna returns a new token. This token is authenticated user token. We use `js-cookie` to store this token in the browser cookies. 

### Creating a new Post

In our application, logged-in users are able to create new posts. Create a new function called `clientWithAuthToken` in your `client.js` file. You can pass in an auth token retrieved from session cookies and this function will set up the GraphQL client with that session token. 

```jsx
// src/client.js

export const clientWithAuthToken = token => createClient({
  url: 'https://graphql.us.fauna.com/graphql',
  fetchOptions: () => {
    console.log('token', token);
    return {
      headers: { authorization: token ? `Bearer ${token}` : '' },
    };
  },
});
```

Next, let’s go ahead and create a page where users can post new posts. 

Create a new file `src/routes/posts/new.svelte` and add the following code in there.

```jsx

// src/routes/posts/new.svelte

<script lang="js">
  import Cookies from 'js-cookie';
  import { setClient, mutation } from '@urql/svelte';
  import { clientWithAuthToken } from '../../client';
  import { goto } from '$app/navigation';

  let userSession = Cookies.get('MY_BLOG_APP_TOKEN');
  let authorId;

  if(userSession) {
    const { secret, id } = JSON.parse(userSession);
    authorId = id;
    setClient(clientWithAuthToken(secret));
  }

  const newPost = mutation({
    query: `
    mutation CreatePost($title: String!, $content: String! $authorId: ID!) {
      createPost(data: {
        title: $title
        content: $content
        author: {
          connect: $authorId
        }
      }) {
        _id
        title
        content
      }
    }
    `,
  });

  async function onSubmit(e) {
    const formData = new FormData(e.target);

    const data = {};
    for (let field of formData) {
      const [key, value] = field;
      data[key] = value;
    }

    const { content, title } = data;
    try {
      console.log('authorId', authorId);
      if(!authorId) {
        alert('You must be logged in to create a post');
        return;
      }
      const resp = await newPost({ title, content, authorId }); 
      if(resp.data.createPost) {
        alert('Post created successfully')
        goto('/')
      }
    } catch (error) {
      console.log(error);
    }
  }
</script>

<div>
  <h3>New Post</h3>
  {#if !userSession}
    <p class="login-promt">You must be logged in to create a post</p>
  {/if}
  <form on:submit|preventDefault={onSubmit} >
    <div class="input-blocks">
      <label for="name">Title</label>
      <input
        type="text"
        name="title"
        value=""
      />
    </div>
    <div class="input-blocks">
      <label for="name">Content</label>
      <textarea
        type="text"
        name="content"
        value=""
      />
    </div>
    <button type="submit">Submit</button>
  </form>
</div>

<style>
  .input-blocks {
    display: flex;
    flex-direction: column;
    max-width: 300px;
    margin-bottom: 1em;
  }
  .login-promt {
    color: coral;
  }
</style>
```

In the previous code block when a user submits the form the `createPost` mutation fires. Notice that we are using the `clientWithAuthToken` to set up your GraphQL client. You retrieve the session token from browser cookies and use it to set up the GraphQL client. If the user is not logged in or if the session token expired then this mutation will not work.

### Deleting a Post

Let’s add the functionality to delete a post. Create a new component `src/lib/Delete.svelte`

and add the following code.

```jsx
// src/lib/Delete.svelte

<script lang="js">
  import Cookies from 'js-cookie';
  import { clientWithAuthToken } from '../client';
  import { setClient, mutation } from '@urql/svelte';
  import { page } from '$app/stores';
  import { goto } from '$app/navigation';

  let userSession = Cookies.get('MY_BLOG_APP_TOKEN');
  if (userSession) {
    setClient(clientWithAuthToken(userSession))
    const {secret } = JSON.parse(userSession);
    setClient(clientWithAuthToken(secret));
  }

  const deletePost = mutation({
    query: `
      mutation DeletePost($id: ID!) {
        deletePost(id: $id) {
          _id
          title
        }
      }
    `
  })

  async function handleDelete() {

    const { data, error } = await deletePost({ id: $page.params.id });

    if(error) {
      console.log('error', error);
      alert('error', error.message);
      return;
    }

    if(data.deletePost) {
      alert('Post deleted');
      goto('/')
    }

  }
</script>

<button on:click|preventDefault={handleDelete} disabled={!userSession}>Delete</button>
```

This component renders a button. When the button is selected it fires the `deletePost` mutation with the authenticated user’s token.

Add this component to your `src/routes/posts/[id].svelte` page.

```jsx
<script lang="js">
 ...
</script>

...
<Delete />
{/if}
```

However, notice that when you select the button you will receive a permission denied message. This is because we haven’t set delete privilege. 

Head over to Fauna dashboard again and select *Security > Roles > UserRole.*

![Defining user roles](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x6xw4fqi66hm3u7abl1h.png)

In the `Post` collection check the delete and select save.

![Post permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k4nodpfxyti8g1mhn7q5.png)

🤔 What if you only want the owner of the post to be able to delete it. It is very easy to add this rule. From the post dropdown select a delete rule.

![Predicate permission](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5vk10c344ijksstkhlg.png)

Add the following code snippet in the predicate rule. This predicate rule defines that only the author of a post is allowed to delete a post.

```clojure
Lambda("ref", Equals(
  Identity(), // logged in user
  Select(["data", "author"], Get(Var("ref")))
))
```

### Editing a post

Next, let’s add the edit post functionality. Go ahead and create a new component `/src/lib/Edit.svelte` and add the following code. 

```jsx
// /src/lib/Edit.svelte
<script lang="js">
  import { operationStore, query, setClient } from '@urql/svelte';
  import { page } from '$app/stores';
	import client from '../../client'
  import Delete from '$lib/Delete.svelte';
  import Edit from '$lib/Edit.svelte';

	setClient(client);

  const currentPost = operationStore(`
    query GetPostById($id: ID!) {
      findPostByID(id: $id) {
        _id
        title
        content
        author {
          email
        }
      }
    }
  `,
  { id: $page.params.id },
  { requestPolicy: 'network-only' }
  )

  query(currentPost)

  export let post = null;

  currentPost.subscribe(({data}) => {
    if(data) {
      post = data.findPostByID;
    }
  })

</script>

{#if $currentPost.fetching}
<p>Loading...</p>
{:else}

<h2>{$currentPost.data.findPostByID.title}</h2>
<p>By <b>{currentPost.data.findPostByID.author.email}</b></p>
<p>{$currentPost.data.findPostByID.content}</p>
<Edit post={post}/>
<Delete />
{/if}
```

This component is a basic form component where the data is pre populated from the `posts/[id].svelte` component. On form submit this component fires the edit post mutation. 

Add this component in your `src/routes/posts/[id].svelte` file.

```jsx
<script lang="js">
	import Edit from '$lib/Edit.svelte'; 
  ... 
  export let post = null;

  currentPost.subscribe(({data}) => {
    if(data) {
      post = data.findPostByID;
    }
  })
</script>

...
<Edit post={post}/>
{/if}
```

With the changes applied the code in your `src/routes/posts/[id].svelte` file should be as follows.

```jsx
// src/routes/posts/[id].svelte
<script lang="js">
  import { operationStore, query, setClient } from '@urql/svelte';
  import { page } from '$app/stores';
	import client from '../../client'
  import Delete from '$lib/Delete.svelte';
  import Edit from '$lib/Edit.svelte';

	setClient(client);

  const currentPost = operationStore(`
    query GetPostById($id: ID!) {
      findPostByID(id: $id) {
        _id
        title
        content
        author {
          email
        }
      }
    }
  `,
  { id: $page.params.id },
  { requestPolicy: 'network-only' }
  )

  query(currentPost)

  export let post = null;

  currentPost.subscribe(({data}) => {
    if(data) {
      post = data.findPostByID;
    }
  })

</script>

{#if $currentPost.fetching}
<p>Loading...</p>
{:else}

<h2>{$currentPost.data.findPostByID.title}</h2>
<p>By <b>{currentPost.data.findPostByID.author.email}</b></p>
<p>{$currentPost.data.findPostByID.content}</p>
<Edit post={post}/>
<Delete />
{/if}
```

### Update template to reflect user authentication state

Currently our app template doesn’t change when user is in logged in state. Let’s change that. 

Create a new file `src/store.js`. Create a new writeable store in this file to hold user session data. Add the following code to this file.

```jsx
import { writable } from 'svelte/store';

export const userSession = writable(null); 
```

Next, whenever user logs in write the user information to this store. Make the following code changes to your `src/routes/login.svelte` file.

```jsx
<script>
  ...
  import { userSession } from '../store';
  ...
  async function onSubmit(e) {
    ...
    if(resp.data.login.data) {
      ...
      userSession.update(() => ({
        email,
        id: resp.data.login.data._id,
        secret: resp.data.login.secret
      }));
      alert('Login Successful');
      goto('/')
    }
  }
</script>
```

Finally update the `src/lib/Nav.svelte` file with he following code. In the following code block we are listening for any changes to the store. If the user is logged in the app renders Logout form else it renders login and register link.

```jsx
<script lang="js">
  import { userSession } from '../store.js';
  import Cookies from 'js-cookie';
  let user;
  userSession.subscribe(val => {
    user = val;
  });

  function logout() {
    userSession.update(() => null);
    Cookies.remove('MY_BLOG_APP_TOKEN');
  }
</script>

<nav>
  <a href="/">Home</a>
  {#if user}
  <!-- svelte-ignore a11y-invalid-attribute -->
  <a href="#" on:click={logout}>Logout</a>
  {:else}
  <a href="/login">Login</a>
  <a href="/register">Register</a>
  {/if}
  <hr />
</nav>
```

## Deployment

### Vercel

We are now ready to make our application live. You can easily deploy a Svelte application with Vercel. Create a new account on Vercel if you haven’t done it already. Next run the following command and follow the instruction.

```bash
npx vercel --prod
```

### Netlify

Follow the article below for Netlify deployment. 

[https://dev.to/danawoodman/deploying-a-sveltekit-app-to-netlify-5dc3](https://dev.to/danawoodman/deploying-a-sveltekit-app-to-netlify-5dc3)

And that’s a wrap. I hope this article was informative and gave you an overall understanding of developing your own full-stack serverless apps with Svelte and GraphQL. If you have any feedback feel free to drop a line in the comment section. If you have any questions feel free to reach out to me on my Twitter @HaqueShadid