Как построить полностековое бессерверное приложение с помощью Svelte и GraphQL

В этом уроке вы научитесь создавать полностековое бессерверное приложение с помощью Svelte.js, GraphQL и Fauna. Вы создадите платформу для ведения блогов, подобную Dev.to, hashnode.com или Medium. Пользователи смогут входить в ваше приложение, создавать новые сообщения, редактировать и удалять свои посты.

Мы будем использовать следующий технологический стек.

  • Svelte.js (Sveltekit)
  • GraphQL
  • Fauna для базы данных
  • Развертывание (Vercel или Netlify)

🤖 Вы можете найти финальный код в следующей ссылке github.

Создание нового приложения Svelte

Сначала создадим новое приложение Svelte. Выполните следующие команды в терминале.

npm init svelte@next blogApp
Войти в полноэкранный режим Выйти из полноэкранного режима

Svelte CLI предоставит вам несколько опций для настройки нашего приложения. Выберите следующие опции.

✔ Which Svelte app template? › Skeleton project

✔ Use TypeScript? … No

✔ Add ESLint for code linting?  Yes

✔ Add Prettier for code formatting? Yes

Запустите наше только что созданное приложение с помощью следующей команды.

cd blogApp
npm i
npm run dev
Войти в полноэкранный режим Выйти из полноэкранного режима

В этом руководстве мы сосредоточимся на функциональности нашего приложения. Мы не будем уделять много времени стилизации. Давайте продолжим и создадим простой компонент Navbar. Создайте новый файл src/lib/Nav.svelte и добавьте в него следующий код.

// src/lib/Nav.svelte

<nav>
  <a href="/">Home</a>
  <a href="/login">Login</a>
  <a href="/register">Register</a>
</nav>
Вход в полноэкранный режим Выйти из полноэкранного режима

Далее создадим файл макета. Создайте новый файл src/routes/__layout.svelte и добавьте в него следующий код.

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

<Nav />
<slot></slot>
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь при запуске приложения компонент Navbar будет появляться на каждой странице.

Настройка клиента Svelte GraphQL

Ваше приложение Svelte будет потреблять бэкенд-сервис GraphQL. Существует множество популярных библиотек, которые вы можете использовать для потребления GraphQL в Svelte. Библиотека @urql/svelte является одной из самых популярных. Давайте продолжим и установим ее.

Выполните следующую команду, чтобы добавить библиотеку в ваш проект.

npm i @urql/svelte --save
Войти в полноэкранный режим Выйти из полноэкранного режима

Далее создайте новый файл src/client.js и добавьте следующий фрагмент кода.

// 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}` : '' },
    };
  },
}); 
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь мы готовы запрашивать данные из бэкенда GraphQL. Давайте продолжим и настроим нашу базу данных.

Настройка базы данных

Создайте новую учетную запись в Fauna, если вы еще этого не сделали. Fauna — это распределенная бессерверная база данных, которая использует встроенный GraphQL API.

Перейдите на приборную панель Fauna и создайте новую базу данных.

Теперь вы готовы определить нашу схему GraphQL. Следующая ULM-диаграмма описывает, как моделировать данные в вашем приложении. В этом приложении у вас есть пользователи, и у каждого пользователя может быть много постов. Это отношение has_many между User и Post.

Вернитесь к коду и создайте новый файл schema.graphql в вашем корневом каталоге. Добавьте следующий код.

# schema.graphql

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

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

type Query {
  listPosts: [Post]
}
Вход в полноэкранный режим Выход из полноэкранного режима

Затем загрузите схему в базу данных Fauna. Перейдите на приборную панель Fauna, выберите GraphQL и импортируйте схему. Импортируйте файл schema.graphql.

Обратите внимание, что после загрузки схемы перед вами откроется игровая площадка GraphQL. Вы можете добавлять, изменять и отлаживать свой GraphQL api с этой игровой площадки.

Давайте продолжим и добавим некоторые данные в нашу базу данных. Создайте нового пользователя, выполнив следующую мутацию на игровой площадке GraphQL.

mutation CreateNewUser {
  createUser(data: {
    username: "shadid"
    email: "shadid120@email.com"
  }) {
    _id
    username
    email
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Аналогично, создайте новый пост. Запустите следующую мутацию на игровой площадке GraphQL, чтобы создать новый пост.

mutation CreatePost {
  createPost(data: {
    title: "Hello worlds"
    content: "Some content"
    author: {
      **connect: "321522241336508481"**
    }
  }) {
    _id
    title
    content
    author {
      email
    }
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что мы использовали поле author > connect. Добавьте сюда идентификатор пользователя из предыдущей мутации. Это свяжет пользователя с постом. Поэтому автором этого поста будет пользователь, которого вы создали в первой мутации.

Запрос данных из приложения Svelte

Давайте продолжим и запросим данные из нашего приложения Svelte. Сначала нам нужно указать роль и сгенерировать ключ для связи нашего фронтенда с базой данных.

Перейдите на приборную панель Fauna. Выберите Security > Roles > New Custom Role.

Дайте вашей роли имя и предоставьте доступ на чтение к коллекциям User и Post. Также предоставьте доступ на чтение к индексу post_author_by_user и индексу listPosts.

Теперь перейдите в раздел Security > Keys > New Key.

Создайте новый ключ для вашей роли SvelteApp.

Затем скопируйте созданный ключ. Создайте новый файл .env в корне вашего приложения и добавьте ключ в качестве переменной окружения.

# .env
VITE_PUBLIC_FAUNA_KEY=<Your Key Here>
Вход в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что этот ключ является открытым, и он будет открыт для внешнего интерфейса. Вот почему роль, связанная с этим ключом, имеет доступ только для чтения.

Теперь на главной странице давайте извлечем все посты из вашей базы данных. Добавьте следующий код в файл src/routes/index.js.

<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>
Вход в полноэкранный режим Выйдите из полноэкранного режима

Перезапустите ваше приложение. Обратите внимание, что теперь вы получаете все посты в корневом URL вашего приложения.

Обратите внимание, что при выборе поста приложение переводит вас на маршрут /post/:id. Вы сможете увидеть отдельные посты в этом маршруте. Давайте продолжим и создадим этот маршрут.

Создайте новый файл routes/posts/[id].svelte и добавьте следующий код.

// 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}
Вход в полноэкранный режим Выход из полноэкранного режима

Аутентификация и авторизация

Далее давайте добавим аутентификацию в наше приложение. Мы можем легко добавить аутентификацию, используя библиотеки fauna-gql-upload и fauna-graphql-tool. Для начала давайте добавим эти зависимости в наш проект.

npm i @fauna-labs/graphql-tool fauna-gql-upload --save-dev
Вход в полноэкранный режим Выход из полноэкранного режима

Эти библиотеки являются скриптами автоматизации, и для запуска этих инструментов вам нужен ключ администратора от Fauna.

Перейдите на приборную панель Fauna.

Выберите Безопасность > Ключи > Новый ключ.

Создайте новый ключ Admin. Убедитесь, что роль установлена как admin.

Не передавайте этот ключ администратора никому и не развертывайте его вместе с вашим приложением. Ключ администратора должен использоваться только с инструментами автоматизации/миграции.

Добавьте ключ администратора в переменную .env. Убедитесь, что ваш файл .env находится в списке gitignore.

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

Войдите в полноэкранный режим Выйдите из полноэкранного режима

Далее необходимо внести следующие изменения в схему GraphQL.

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]
}
Войти в полноэкранный режим Выход из полноэкранного режима

Обратите внимание, что в предыдущем блоке кода мы добавили директиву @auth к нашей коллекции User. Это означает, что мы будем использовать коллекцию User для аутентификации. Ключ primary определяет, какие поля будут использоваться для регистрации и входа пользователей. В данном случае это email. Поэтому пользователи могут войти в систему, используя свой email и пароль.

Обратите внимание, что в коллекцию Post добавлена директива @protected.* Эта директива определяет шаблоны доступа. Вошедшим пользователям разрешено писать, создавать новые посты.

После добавления этих изменений в схему откройте файл package.json и добавьте следующий фрагмент кода в раздел script.

// package.json

{
 ...
 "script": {
   ...
   "fgu": "fgu",
   "fgt": "fgt"
 }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Мы добавляем эти скрипты сюда, чтобы мы могли запускать fauna-graphql-tool (fgt) и fauna-gql-upload (fgu) из npm.

fgt берет вашу схему GraphQL и компилирует схему в различные ресурсы базы данных (т.е. коллекции, функции, определяемые пользователем, правила аутентификации), а fgu загружает ресурсы в Fauna.

Наконец, выполните следующую команду в терминале

npm run fgt && npm run fgu
Войти в полноэкранный режим Выйти из полноэкранного режима

Обратите внимание, что создается новая папка/fauna со всеми ресурсами.

  • 📗 Pro Tip:

    Обратите внимание, что при запуске скриптов создается новая папка /fauna. Вы можете открыть эту папку и посмотреть на различные функции и роли, созданные сценариями автоматизации. Если вы хотите дополнительно настроить правила аутентификации, не стесняйтесь изменить здесь параметр logic.

Если вам интересно, как работают эти ресурсы, ознакомьтесь с документацией по драйверам Fauna JavaScript.

Теперь, когда вы вернетесь к GraphQL playground в Fauna, вы заметите, что вам доступны мутации register и login.

Наконец, перейдите в раздел Security > Roles > SvelteRole и предоставьте вашей роли привилегии вызова для этих недавно созданных функций. Не забудьте также предоставить доступ на чтение к индексу user_by_email, поскольку этот индекс используется функцией login.

Форма регистрации пользователя

Далее, давайте продолжим создание формы регистрации пользователя. Создайте новый файл src/routes/register.svelte и добавьте следующий код.

// 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>
Вход в полноэкранный режим Выход из полноэкранного режима

В предыдущем блоке кода у вас есть простой компонент формы. При отправке формы выполняется мутация register и регистрируется новый пользователь.

Форма входа пользователя в систему

Далее давайте создадим форму входа пользователя в систему. Мы можем сохранить сессию пользователя в cookies браузера. Библиотека js-cookie позволит нам сделать это легко. Добавьте эту библиотеку, выполнив следующую команду в терминале.

npm i js-cookie --save
Войти в полноэкранный режим Выйти из полноэкранного режима

Создайте новый файл src/routes/login.svelte и добавьте следующий код.

<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>
Вход в полноэкранный режим Выйти из полноэкранного режима

В предыдущем блоке кода у вас есть компонент простой формы. При отправке формы срабатывает мутация login. При успешном входе в систему Fauna возвращает новый токен. Этот токен является токеном аутентифицированного пользователя. Мы используем js-cookie для хранения этого токена в cookies браузера.

Создание нового поста

В нашем приложении авторизованные пользователи могут создавать новые посты. Создайте новую функцию clientWithAuthToken в вашем файле client.js. Вы можете передать токен аутентификации, полученный из cookies сессии, и эта функция установит клиент GraphQL с этим токеном сессии.

// 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}` : '' },
    };
  },
});
Вход в полноэкранный режим Выход из полноэкранного режима

Далее давайте создадим страницу, на которой пользователи смогут публиковать новые сообщения.

Создайте новый файл src/routes/posts/new.svelte и добавьте в него следующий код.


// 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>
Вход в полноэкранный режим Выйти из полноэкранного режима

В предыдущем блоке кода, когда пользователь отправляет форму, срабатывает мутация createPost. Обратите внимание, что мы используем clientWithAuthToken для настройки вашего клиента GraphQL. Вы получаете токен сессии из cookies браузера и используете его для настройки клиента GraphQL. Если пользователь не вошел в систему или если срок действия токена сессии истек, то эта мутация не сработает.

Удаление сообщения

Давайте добавим функциональность для удаления поста. Создайте новый компонент src/lib/Delete.svelte

и добавьте следующий код.

// 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>
Вход в полноэкранный режим Выйти из полноэкранного режима

Этот компонент отображает кнопку. Когда кнопка выбрана, она запускает мутацию deletePost с токеном аутентифицированного пользователя.

Добавьте этот компонент на страницу src/routes/posts/[id].svelte.

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

...
<Delete />
{/if}
Вход в полноэкранный режим Выход из полноэкранного режима

Однако обратите внимание, что при выборе кнопки вы получите сообщение об отказе в разрешении. Это происходит потому, что мы не установили привилегию delete.

Снова перейдите в Fauna dashboard и выберите Security > Roles > UserRole.

В коллекции Post отметьте галочкой пункт delete и выберите save.

🤔 Что если вы хотите, чтобы только владелец поста мог удалить его. Добавить такое правило очень просто. Из выпадающего списка постов выберите правило удаления.

Добавьте следующий фрагмент кода в правило предиката. Это правило предиката определяет, что удалять сообщение может только автор сообщения.

Lambda("ref", Equals(
  Identity(), // logged in user
  Select(["data", "author"], Get(Var("ref")))
))
Вход в полноэкранный режим Выход из полноэкранного режима

Редактирование сообщения

Далее добавим функцию редактирования сообщения. Создайте новый компонент /src/lib/Edit.svelte и добавьте следующий код.

// /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}
Вход в полноэкранный режим Выйти из полноэкранного режима

Этот компонент представляет собой базовый компонент формы, где данные предварительно заполняются из компонента posts/[id].svelte. При отправке формы этот компонент запускает мутацию редактирования поста.

Добавьте этот компонент в файл src/routes/posts/[id].svelte.

<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}
Вход в полноэкранный режим Выйдите из полноэкранного режима

После внесения изменений код в вашем файле src/routes/posts/[id].svelte должен выглядеть следующим образом.

// 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}
Вход в полноэкранный режим Выход из полноэкранного режима

Обновление шаблона для отражения состояния аутентификации пользователя

В настоящее время шаблон нашего приложения не изменяется, когда пользователь находится в состоянии входа в систему. Давайте изменим это.

Создайте новый файл src/store.js. Создайте в этом файле новый записываемый магазин для хранения данных пользовательской сессии. Добавьте в этот файл следующий код.

import { writable } from 'svelte/store';

export const userSession = writable(null); 
Вход в полноэкранный режим Выйти из полноэкранного режима

Далее, каждый раз, когда пользователь входит в систему, записывайте информацию о нем в это хранилище. Внесите следующие изменения в файл src/routes/login.svelte.

<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>
Войти в полноэкранный режим Выйдите из полноэкранного режима

Наконец, обновите файл src/lib/Nav.svelte следующим кодом. В следующем блоке кода мы слушаем любые изменения в магазине. Если пользователь вошел в систему, приложение отображает форму Logout, в противном случае оно отображает ссылку для входа и регистрации.

<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>
Вход в полноэкранный режим Выход из полноэкранного режима

Развертывание

Vercel

Теперь мы готовы к развертыванию нашего приложения. Вы можете легко развернуть приложение Svelte с помощью Vercel. Создайте новую учетную запись на Vercel, если вы еще не сделали этого. Затем выполните следующую команду и следуйте инструкциям.

npx vercel --prod
Войти в полноэкранный режим Выйти из полноэкранного режима

Netlify

Для установки Netlify следуйте приведенной ниже статье.

https://dev.to/danawoodman/deploying-a-sveltekit-app-to-netlify-5dc3

Вот и все. Надеюсь, эта статья была информативной и дала вам общее представление о разработке собственных полнофункциональных бессерверных приложений с помощью Svelte и GraphQL. Если у вас есть какие-либо замечания, не стесняйтесь оставить строчку в разделе комментариев. Если у вас есть вопросы, не стесняйтесь обращаться ко мне в Twitter @HaqueShadid

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *