Как инициализировать 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. Обратите внимание на то, как он импортирован ниже, чтобы проект был успешно построен с адаптером узла.
Попробуйте выполнить следующие шаги.
- Создайте новое приложение SvelteKit и выберите «Демонстрационное приложение SvelteKit» на первом шаге мастера установки SvelteKit. Ответьте на вопрос «Использовать TypeScript?» вопрос с
N
а также все вопросы после.
npm init svelte@next demo-app
cd demo-app
- Измените
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"
}
}
- Измените
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 || {})
}
}
}
};
- Создать
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;
- Создать
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'
}
}
}
- Добавьте следующее в
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);
}
- Наконец выполнить
npm install
а такжеnpm run dev
команды. Загрузите сайт в свой веб-браузер и увидите маршрут на стороне сервера, запрашиваемый клиентом всякий раз, когда вы наводите курсор наTODOS
ссылка на навигационной панели. На вкладке сети консоли обратите внимание, насколько быстрее отклик отtest
маршрут на каждую секунду и последующий запрос благодаря Apolloclient
экземпляр является синглтоном.
При использовании вышеупомянутого решения 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 и создать
Возможно, не оптимальное решение, но оно решает проблемы кеширования и авторизации.