Как инициализировать ApolloClient в SvelteKit для работы как на SSR, так и на стороне клиента

Я пробовал, но не получилось. Получил ошибку: Ошибка при оценке модуля SSR /node_modules/cross-fetch/dist/browser-ponyfill.js:

      <script lang="ts">
import fetch from 'cross-fetch';
import { ApolloClient, InMemoryCache, HttpLink } from "@apollo/client";

const client = new ApolloClient({
    ssrMode: true,
    link: new HttpLink({ uri: '/graphql', fetch }),
    uri: 'http://localhost:4000/graphql',
    cache: new InMemoryCache()
  });
</script>

2 ответа

В SvelteKit тема CSR и SSR и место, где должна происходить выборка данных, немного глубже, чем с другими в чем-то «похожими» решениями. Приведенное ниже руководство должно помочь вам соединить некоторые точки, но сначала необходимо указать пару вещей.

Чтобы определить маршрут на стороне сервера, создайте файл с расширением в любом месте src/routesдерево каталогов. Этот .js В файле могут быть все необходимые операторы импорта без отправки в веб-браузер связанных с ними пакетов JS.

Это довольно большой, поскольку он содержит reactзависимость. Вместо этого вы можете захотеть импортировать только @apollo/client/coreдаже если вы настраиваете Apollo Client для использования только на стороне сервера, как показано в демонстрации ниже. В @apollo/clientне является пакетом ESM. Обратите внимание на то, как он импортирован ниже, чтобы проект был успешно построен с адаптером узла.

Попробуйте выполнить следующие шаги.

  1. Создайте новое приложение SvelteKit и выберите «Демонстрационное приложение SvelteKit» на первом шаге мастера установки SvelteKit. Ответьте на вопрос «Использовать TypeScript?» вопрос с N а также все вопросы после.
      npm init svelte@next demo-app
cd demo-app
  1. Измените package.jsonсоответственно. При желании проверьте наличие обновлений всех пакетов с помощью npx npm-check-updates -u
      {
    "name": "demo-app",
    "version": "0.0.1",
    "scripts": {
        "dev": "svelte-kit dev",
        "build": "svelte-kit build --verbose",
        "start": "svelte-kit start"
    },
    "devDependencies": {
        "@apollo/client": "^3.3.15",
        "@sveltejs/adapter-node": "next",
        "@sveltejs/kit": "next",
        "graphql": "^15.5.0",
        "svelte": "^3.37.0",
        "vite": "^2.2.0"
    },
    "type": "module",
    "dependencies": {
        "@fontsource/fira-mono": "^4.2.2",
        "@lukeed/uuid": "^2.0.0",
        "cookie": "^0.4.1"
    }
}
  1. Измените svelte.config.cjs соответственно.
      const node = require('@sveltejs/adapter-node');
const pkg = require('./package.json');

/** @type {import('@sveltejs/kit').Config} */
module.exports = {
    kit: {
        // By default, `npm run build` will create a standard Node app.
        // You can create optimized builds for different platforms by
        // specifying a different adapter
        adapter: node(),

        // hydrate the <div id="svelte"> element in src/app.html
        target: '#svelte',

        vite: {
            ssr: {
                external: Object.keys(pkg.dependencies || {})
            }
        }
    }
};

  1. Создать src/lib/Client.jsфайл со следующим содержимым. Это установочный файл Apollo Client.
      import { ApolloClient, HttpLink } from '@apollo/client/core/core.cjs.js';
import { InMemoryCache } from '@apollo/client/cache/cache.cjs.js';

class Client {
    constructor() {
        if (Client._instance) {
            return Client._instance
        }
        Client._instance = this;

        this.client = this.setupClient();
    }

    setupClient() {
        const link = new HttpLink({
            uri: 'http://localhost:4000/graphql',
            fetch
        });

        const client = new ApolloClient({
            link,
            cache: new InMemoryCache()
        });
        return client;
    }
}

export const client = (new Client()).client;

  1. Создать src/routes/gry/test.jsсо следующим содержанием. Это маршрут на стороне сервера. Если в схеме graphql нет double функция определяет другой запрос, ввод (ы) и вывод.
      import { client } from '$lib/Client.js';
import { gql } from '@apollo/client/core/core.cjs.js';

export const post = async request => {
    // mitigate the body parsing bug
    if (typeof request.body === 'string') request.body = JSON.parse(request.body);

    const { num } = request.body;

    try {
        const query = gql`
            query Doubled($x: Int) {
                double(number: $x)
            }
        `;
        const result = await client.query({
            query,
            variables: { x: num }
        });

        return {
            status: 200,
            body: {
                nodes: result.data.double
            }
        }
    } catch (err) {
        return {
            status: 500,
            error: 'Error retrieving data'
        }
    }
}

  1. Добавьте следующее в load функция routes/todos/index.svelte файл в <script context="module">...</script> тег.
          try {
        const res = await fetch('/qry/test', {
            method: 'POST',
            credentials: 'same-origin',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({
                num: 19
            })
        });
        const data = await res.json();
        console.log(data);
    } catch (err) {
        console.error(err);
    }
  1. Наконец выполнить npm install а также npm run devкоманды. Загрузите сайт в свой веб-браузер и увидите маршрут на стороне сервера, запрашиваемый клиентом всякий раз, когда вы наводите курсор на TODOSссылка на навигационной панели. На вкладке сети консоли обратите внимание, насколько быстрее отклик от test маршрут на каждую секунду и последующий запрос благодаря Apollo client экземпляр является синглтоном.

При использовании вышеупомянутого решения phaleth следует иметь в виду две вещи: кеширование и аутентифицированные запросы.

Поскольку клиент используется в конечной точке /qry/test.js, одноэлементный шаблон с поведением кэширования обеспечивает отслеживание состояния вашего сервера. Итак, если A, то B выполнит тот же запрос, B может в конечном итоге увидеть некоторые из данных A.

Та же проблема, если вам нужны заголовки авторизации в вашем запросе. Вам нужно будет настроить это в методе setupClient следующим образом

      setupClient() {
  ...

  const authLink = setContext((_, { headers }) => {
    return {
      headers: {
        ...headers,
        authorization: `Bearer ${sometoken}`
      }
    };
  });

  const client = new ApolloClient({
    credentials: 'include',
    link: authLink.concat(link),
    cache: new InMemoryCache()
  });
}

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

Чтобы сохранить сервер без состояния, можно обойтись без шаблона singleton и создать в конечной точке по каждому запросу.

Возможно, не оптимальное решение, но оно решает проблемы кеширования и авторизации.

Другие вопросы по тегам