Open shashkovdanil opened 5 years ago
I think i also need this it's not completely clear from the sapper example. Be great to see the setup using the stock sapper template!
I had been waiting for the various sapper updates to land for svelte v3 before looking into this, but it looks like I missed the memo and it's been released with v0.26 of sapper. I'll take another look at this soon.
Would like to see this as well!
Apologies for being "that person", but are there any updates on this topic or guidance in the meantime? I have been unable to find a good way of configuring this with Sapper
UPDATE:
I got this working by using _layout.svelte
as the point to initialise and set the Client we can then use getClient
in each route that uses this layout.
_layout.svelte
:
<script>
import ApolloClient from "apollo-boost";
import { setClient } from "svelte-apollo";
const client = new ApolloClient({
uri: "https://..."
});
setClient(client);
</script>
...
index.svelte
:
<script>
import { getClient, query } from 'svelte-apollo';
import { GET_BOOKS } from './queries';
// 2. Get the Apollo client from context
const client = getClient();
// 3. Execute the GET_BOOKS graphql query using the Apollo client
// -> Returns a svelte store of promises that resolve as values come in
const books = query(client, { query: GET_BOOKS });
</script>
<!-- 4. Use $books (note the "$"), to subscribe to query values -->
{#await $books}
Loading...
{:then result}
{#each result.data.books as book}
{book.title} by {book.author.name}
{/each}
{:catch error}
Error: {error}
{/await}
This doesn't integrate with the Sapper router, but we can use onMount
to perform re-fetches etc. I am yet to try SSR.
I took the same approach with _layout.svelte and not just for the svelte-apollo client. Except I put all of that setup into another module (setup.js) and imported from _layout. I just couldn't stomach having all that code actually in my _layout file. It's for layout, supposedly, but it's the only component that is a parent to the whole app.
@jschuhr I agree. I am sure this could be done in client.js
and server.js
but, I myself, do not know how. This was the easiest way forward until this lib integrates with the Sapper primitives.
@jthegedus when I try to setClient
in _layout
and then later getClient
I get a message: Function called outside component initialization
.
The way I get around this is by importing the client directly into the component that calls it. Is this a problem with Svelte's context?
@jthegedus when I try to
setClient
in_layout
and then latergetClient
I get a message:Function called outside component initialization
.The way I get around this is by importing the client directly into the component that calls it. Is this a problem with Svelte's context?
Same issue here:
Error: Function called outside component initialization
at get_current_component (/git/node_modules/svelte/internal/index.js:520:15)
at Object.setContext (/git/node_modules/svelte/internal/index.js:550:5)
at Object.setClient (/git/node_modules/svelte-apollo/dist/svelte-apollo.cjs.js:15:12)
at create_ssr_component (/git/__sapper__/dev/server/server.js:10007:16)
at Object.$$render (/git/__sapper__/dev/server/server.js:230:22)
at create_ssr_component (/git/__sapper__/dev/server/server.js:10182:41)
at $$render (/git/__sapper__/dev/server/server.js:230:22)
at Object.render (/git/__sapper__/dev/server/server.js:238:26)
at handle_page (/git/__sapper__/dev/server/server.js:12560:36)
I used the onMount hook around the setClient(), maybe that helps. EDIT: The error reappears when calling the getClient() EDIT 2: Fixed, I imported the child before setClient()!
I used the onMount hook around the setClient(), maybe that helps. EDIT: The error reappears when calling the getClient() EDIT 2: Fixed, I imported the child before setClient()!
Cool. Can you provide an example with some code? In which file did you put the setClient()? Where and how did you call the getClient()? Did you need to use the onMount-Function or the
getClient in routes/some_route/index.svelte
```js
Not sure I did anything special here other than not use getClient
in routes/index.svelte
@jthegedus @phi1-h @F7dev @jschuhr @timhall I'm desperately looking to solve this problem, this is a huge show stopper in adopting GraphQL in my sapper project and would love to contribute a template project that works for Sapper. The only thing is I tried everything mentioned in all the issues across this repo and I'm still stranded and seriously tired. I'm forced to use other libraries for PoC for query
only, but I wanna exploit the core capabilities.
Hi @jerriclynsjohn I don't believe I have done anything special. The only thing of note was that the index route doesn't use the svelte-apollo
client
, so anything done in _layout.svelte
is completely evaluated before I would route elsewhere where client
is fetched and used.
Hey there! I've just found the same issue and tried the above solutions, but they didn't help me much. After having tried them, I considered avoiding setClient
and getClient
altogether and simply importing the instantiated client from another module with the hope that the client instance would be reused because of how (I think) modules work.
It doesn't throw anymore and it's working fine so far, but I thought I'd ask people more knowledgeable in Svelte about the consequences of such an approach.
To be clear, there are some sample files:
src/data/client.js
src/routes/index.svelte
{JSON.stringify(companies)}{:catch error}
What am I missing? What are the downsides of such approach?
Without a proper walkthrough of setting this up, we are gonna be stuck in this boat for a while
Greetings. I am an astronaut from the future. I've just landed on the moon via the Apollo spacecraft only to encounter this issue.
I gave up trying to make setClient()
work on Sapper so I just did something like:
_layout.svelte
<script context="module">
import client, { EVERYTHING } from '../../lib/apollo.js';
export async function preload() { return { cache: await client.query({ query: EVERYTHING }) }; }
<Component {cache} {client} />
- `Component.svelte`
{#await $preferences} Loading... {:then result} ... {/await}
I'm not sure if I'm doing this correctly. I've just discovered Svelte a few weeks ago.
Here's a related thread on the Sapper issue tracker that's also helpful: https://github.com/sveltejs/sapper/issues/751
Is everyone here just doing client-side fetching? Or has anyone gotten this to work on the server-side as well?
Is anyone using Sapper's this.fetch
? If not, do you still have the request headers from the original request carried over to pass cookies and auth headers?
I'm doing server and client-side. Am not currently using this.fetch, though that's mostly because I forgot it existed.
I have a middleware that extracts the bearer token from the session (on both client and server) and adds it as an authorization header in the fetch request.
This approach isn't perfect but it works reasonably well. At some point I will probably use the cache from the server request to the client by passing it in the session object.
I had the same problem and was able to work around the limitations using the context api. Sharing is caring, so I hope this helps somebody:
// _layout.svelte
<script context="module">
import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import { RetryLink } from "apollo-link-retry";
export async function preload(page, session) {
const httpLink = createHttpLink({
// Neccessary because it is a json property which is a string
uri: `http${process.env.BACKEND_SSL ? "s" : ""}://${
process.env.SERVER_NAME
}:${process.env.SERVER_PORT}/graphql`,
credentials: "include",
fetch: this.fetch
});
const retryLink = new RetryLink({
attempts: { max: 3 },
delay: { initial: 200 }
});
const link = retryLink.concat(httpLink);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
name: "Some Website",
version: "3.0"
});
return { client };
}
</script>
<script>
import { setContext } from "svelte";
export let client;
setContext("client", client);
</script>
// component.svelte
<script>
import { getContext } from "svelte";
const client = getContext("client");
</script>
Based on my reading of https://sapper.svelte.dev/docs#Preloading I expect
the { client }
return value from preload to fail serialization with deval.
It seems like returning client
from preload will (silently?) fail
serialization of that page's preload values.
Doesn't seem to matter for the exact example you've given, but perhaps this fact will help someone else trying to tease apart why they're seeing preload run once on the server and then again in the browser in their own application.
On Thu, Jun 18, 2020 at 6:54 AM Pascal Clanget notifications@github.com wrote:
I had the same problem and was able to work around the limitations using the context api. Sharing is caring, so I hope this helps somebody:
// _layout.svelte
// component.svelte
— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/timhall/svelte-apollo/issues/9#issuecomment-646031442, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAABZHNLXPBMQBIVTYOB64LRXIL73ANCNFSM4HJOYRXQ .
re: Function called outside component initialization
this happens with getClient and setClient because it is a svelte context which is only available at component initialization (construction) and cannot be in an event handler. see: the answer for https://stackoverflow.com/questions/60591927/svelte-user-registration-issue-with-setting-store-value Most likely what is happening is that you're calling it in an async event su as an on:click, onMount, onDestroy when it is not available. The solution to this is to store your client as a store in the context as per https://svelte.dev/tutorial/context-api.
In general it should probably look something like:
setClient(writable(apolloClient));
let client
getClient().subscribe((_client) => client = _client);
I would just like to share this here. I made this about a year ago, and just recently updated it. Sapper start
aside, I believe it serves well as an example of SSR, setClient etc.
I would just like to share this here. I made this about a year ago, and just recently updated it.
Sapper start
aside, I believe it serves well as an example of SSR, setClient etc.
👉 https://github.com/timhall/svelte-apollo/issues/59 👈 👀
I just use apollo/core
Using this package with a generator like this https://github.com/ticruz38/graphql-codegen-svelte-apollo, could resolve most of your issues when working with Sapper. I think the client side rehydration is not that important, worst case you'll end up making 2 queries instead of one, your page will be server side rendered with your data anyway... Last version of this package broke the sapper compatibility however... https://github.com/ticruz38/graphql-codegen-svelte-apollo/issues/2
https://bjornlu.com/blog/using-apollo-client-in-sapper/
This approach works well. On the server it writes the cache into the session and on the client replaces it with a new apollo client with the cache data from the server.
But the code in the link is not 100% working. Just apply the following:
Add apollo client a bit differently to the session
import fetch from 'cross-fetch'
...
sapper.middleware({
session: (req) => {
const apollo = new ApolloClient({
ssrMode: true,
link: createHttpLink({
uri: 'your-endpoint-uri',
credentials: 'same-origin',
fetch
}),
cache: new InMemoryCache()
})
return {
apollo
}
}
})
also make sure you check if in the preload before you do a call it is on the server (preload runs on server and client)
<script context="module">
export async function preload(page, session) {
const YOUR-QUERY = gql`
query yourQuery{
...
}
`
if (!process.browser) {
const { apollo } = session
const data = await apollo.query({ query: YOUR-QUERY })
}
}
</script>
The rest is well desribed in the link above.
With the transition towards Svelte Kit, maybe such example would be ideal to look at as well.
@nico-on-vacation, I have tried to set up Apollo Client in my Sapper project just like you described but sadly, all I can get is the following error: Failed to serialize session data: Cannot stringify arbitrary non-POJOs
. Does anyone have an idea what's wrong and how it could be fixed?
Please add full example svelte-apollo with sapper v3. I'm having trouble with this: