svelte-apollo icon indicating copy to clipboard operation
svelte-apollo copied to clipboard

Usage With Svelte Kit

Open stolinski opened this issue 3 years ago • 22 comments

<script>
    import Header from '$lib/layout/Header.svelte'
    import Footer from '$lib/layout/Footer.svelte'
    import '$lib/style/styles.scss'

    import { setClient } from "svelte-apollo";
    import { ApolloClient, HttpLink } from '@apollo/client/core/core.cjs.js'
    import { InMemoryCache } from '@apollo/client/cache/cache.cjs.js'

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

    const client = new ApolloClient({
        link,
        cache: new InMemoryCache()
    })
    console.log('client', client);
    setClient(client);
</script>

<Header />

<slot />

<Footer />

In our __layout.svelte Produces [HMR][Svelte] Unrecoverable error in <__layout>: next update will trigger a full reload logError @ proxy.js:15 Proxy<__layout> @ proxy.js:372 create_fragment @ root.svelte? [sm]:36 init @ index.mjs?v=e94d9004:1500 Root @ root.svelte? [sm]:16 createProxiedComponent @ svelte-hooks.js:245 ProxyComponent @ proxy.js:241 Proxy<Root> @ proxy.js:341 _init @ start.js:646 start @ start.js:530 async function (async) start @ start.js:483 start @ start.js:1096 (anonymous) @ (index):39

Removing setClient(client) fixes error, breaks apollo (obviously). Any thoughts?

Uncaught (in promise) Error: Function called outside component initialization
    at get_current_component (index.mjs:649)
    at setContext (index.mjs:679)
    at setClient (context.ts:19)
    at instance (__layout.svelte? [sm]:19)
    at init (index.mjs?v=e94d9004:1485)
    at new _layout (__layout.svelte? [sm]:19)
    at createProxiedComponent (svelte-hooks.js:245)
    at new ProxyComponent (proxy.js:241)
    at new Proxy<__layout> (proxy.js:341)
    at create_fragment (root.svelte? [sm]:36)

stolinski avatar Jun 02 '21 21:06 stolinski

Hey Scott, I've seen this a couple times but in my case it's only happening when called from <script context='module'>. Is this what you're seeing?

Ignore this, I don't know if I missed it or something changed, I am seeing this everywhere now.

moonmeister avatar Jun 07 '21 16:06 moonmeister

I'm trying to track this down. I did find https://github.com/sveltejs/sapper/issues/592 which might be relevant but I'm not entirely sure.

Clarification on the many uses of "context" between svelte and svelte kit: https://github.com/sveltejs/kit/issues/984

I think an important thing I recognized from these two is that svelte's getContext / setContext will never work with Svelte Kit's load function cause it's called in the module context. This SHOULD still work in the normal <script> context though. Why it doesn't is possibly a bug or maybe a part of how SK works.

Update: Scott, I just realize I assumed you are using svelte kit, are you? If not, my bad for hijacking your issue here.

moonmeister avatar Jun 07 '21 22:06 moonmeister

This is vite not deduping svelte correctly and using two instances of svelte thus losing track of the current component and context.

One workaround is to alias svelte-apollo to trick vite into thinking it's part of our code and not a node_module to force it to use our already installed svelte dependency.

Here's the config path in svelte.config.js

const config = {
  kit: {
    vite: {
      resolve: {
        alias: {
          'svelte-apollo': '/node_modules/svelte-apollo/dist/svelte-apollo.es.js'
        },
      },
    }
  }
};

Another trick to make it work well with sveltekit is to create a wrapper component around the layout slot then instantiate the client inside the wrapper component.

unlocomqx avatar Jun 08 '21 06:06 unlocomqx

Putting this into the vite config should work too:

optimizeDeps: {
  include: [
    "@apollo/client/core",
    "@apollo/client/cache",
    "@apollo/client/link/ws",
    "@apollo/client/link/context",
    "@apollo/client/link/error",
    "@apollo/client/utilities",
  ],
  exclude: ["@apollo/client", "svelte-apollo"],
},

bluwy avatar Jun 09 '21 05:06 bluwy

Getting stuck here as well. The workaround from @unlocomqx allowed use of setClient, but now using mutation throws the same error.

cschmatzler avatar Jun 11 '21 15:06 cschmatzler

When I gave up trying to use this library everything started working. My understanding is this lib was designed to be used in svelte components and is incompatible with and possibly even redundant to using the load api. I haven't explored. In my layout component I placed my client into the load API context and accessed it from there on all other pages.

moonmeister avatar Jun 11 '21 15:06 moonmeister

@cschmatzler Yeah the current design only allows making a query or mutation during component initialisation because it's coupled with svelte context (which only allows reading/writing during component initialisation)

The solution is to use lazy queries according to the lib author which is still unimplemented so this is currently definitely unusable https://github.com/timhall/svelte-apollo/issues/53#issuecomment-729357088

unlocomqx avatar Jun 11 '21 15:06 unlocomqx

FWIW I've written a guide for Sapper integration some time ago, which should still work in SvelteKit. Haven't got the time to do a refresh on it. Note: It uses the Apollo client directly.

bluwy avatar Jun 11 '21 15:06 bluwy

I think it's better to just encapsulate the module functions in the client directly I made an attempt here https://github.com/unlocomqx/svelte-apollo/blob/patch-client/src/client.ts

I can now create the client in a separate file and export it, then import it wherever needed

client.query(...);
client.mutate(...);

Much better than dealing with the context because it's very limiting

unlocomqx avatar Jun 11 '21 16:06 unlocomqx

I get this error. Hope someone has a solution for that:

 > node_modules/@apollo/client/react/hooks/useReactiveVar.js:1:36: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    1 │ import { useEffect, useState } from 'react';
      ╵                                     ~~~~~~~

 > node_modules/@apollo/client/react/context/ApolloProvider.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    2 │ import * as React from 'react';
      ╵                        ~~~~~~~

 > node_modules/@apollo/client/react/hooks/useApolloClient.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    2 │ import * as React from 'react';
      ╵                        ~~~~~~~

 > node_modules/@apollo/client/react/context/ApolloConsumer.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    2 │ import * as React from 'react';
      ╵                        ~~~~~~~

 > node_modules/@apollo/client/react/context/ApolloContext.js:1:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    1 │ import * as React from 'react';
      ╵                        ~~~~~~~

 > node_modules/@apollo/client/react/hooks/useSubscription.js:3:68: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    3 │ import { useContext, useState, useRef, useEffect, useReducer } from 'react';
      ╵                                                                     ~~~~~~~

 > node_modules/@apollo/client/react/hooks/useMutation.js:2:56: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    2 │ import { useContext, useState, useRef, useEffect } from 'react';
      ╵                                                         ~~~~~~~

 > node_modules/@apollo/client/react/hooks/utils/useBaseQuery.js:3:58: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    3 │ import { useContext, useEffect, useReducer, useRef } from 'react';
      ╵                                                           ~~~~~~~

error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    2 │ import { useEffect, useRef } from "react";
      ╵                                   ~~~~~~~

 > node_modules/@apollo/client/react/hooks/utils/useDeepMemo.js:1:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
    1 │ import { useRef } from 'react';
      ╵                        ~~~~~~~

> Build failed with 10 errors:
node_modules/@apollo/client/react/context/ApolloConsumer.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/context/ApolloContext.js:1:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/context/ApolloProvider.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/hooks/useApolloClient.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/hooks/useMutation.js:2:56: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
...
Error: Build failed with 10 errors:
node_modules/@apollo/client/react/context/ApolloConsumer.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/context/ApolloContext.js:1:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/context/ApolloProvider.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/hooks/useApolloClient.js:2:23: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
node_modules/@apollo/client/react/hooks/useMutation.js:2:56: error: Could not resolve "react" (mark it as external to exclude it from the bundle)
...
    at failureErrorWithLog (D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:1478:15)
    at D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:1136:28
    at runOnEndCallbacks (D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:926:63)
    at buildResponseToResult (D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:1134:7)
                                                                                               at D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:1243:14
    at D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:614:9
    at handleIncomingPacket (D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:711:9)
    at Socket.readFromStdout (D:\dev\web\tests\crm\node_modules\esbuild\lib\main.js:581:7)
    at Socket.emit (events.js:400:28)
    at Socket.emit (domain.js:470:12)

package.json

{
	"name": "~TODO~",
	"version": "0.0.1",
	"scripts": {
		"dev": "svelte-kit dev",
		"build": "svelte-kit build",
		"preview": "svelte-kit preview",
		"check": "svelte-check --tsconfig ./tsconfig.json",
		"check:watch": "svelte-check --tsconfig ./tsconfig.json --watch",
		"lint": "prettier --ignore-path .gitignore  --check --plugin-search-dir=. . && eslint --ignore-path .gitignore .",
		"format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. ."
	},
	"devDependencies": {
		"@sveltejs/kit": "next",
		"@typescript-eslint/eslint-plugin": "^4.19.0",
		"@typescript-eslint/parser": "^4.19.0",
		"eslint": "^7.22.0",
		"eslint-config-prettier": "^8.1.0",
		"eslint-plugin-svelte3": "^3.2.0",
		"prettier": "~2.2.1",
		"prettier-plugin-svelte": "^2.2.0",
		"svelte": "^3.34.0",
		"svelte-check": "^2.0.0",
		"svelte-preprocess": "^4.0.0",
		"tslib": "^2.0.0",
		"typescript": "^4.0.0"
	},
	"type": "module",
	"dependencies": {
		"@apollo/client": "^3.4.11",
		"graphql": "^15.5.3",
		"svelte-apollo": "^0.4.0"
	}
}

draylegend avatar Sep 13 '21 11:09 draylegend

@vladimirdrayling Try adding @apollo/client to optimizeDeps.exclude and avoid importing @apollo/client. Import from @apollo/client/core instead.

bluwy avatar Sep 13 '21 12:09 bluwy

Btw the error originally reported has been fixed in vite-plugin-svelte now. The library should work ootb, except for the @apollo/client part (reason). So I'd say this can be closed.

bluwy avatar Sep 13 '21 15:09 bluwy

This is not entirely related to the svelte-apollo package but for those of you that are having trouble setting this up with @apollo/client, I might have a solution.

Credit goes to Reddit user: u/NoahVersace, link to post. It's a fix for Prismic but the same principles apply to @apollo/client.

Here's how I got it working for Apollo: I'm using the svelte static adapter and this works with Apollo on build and dev

// apolloClient.js
import Apollo, * as ApolloScope from '@apollo/client/core/core.cjs.js';

const HttpLink = Apollo?.HttpLink || ApolloScope?.HttpLink;

const ApolloClient = Apollo?.ApolloClient || ApolloScope?.ApolloClient;

const InMemoryCache = Apollo?.InMemoryCache || ApolloScope?.InMemoryCache;

const link = new HttpLink({
	uri: `YOUR_GRAPHQL_ENDPOINT`
});

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
	link,
	cache
});

export default apolloClient;

From here on you may use the client as you will.

For instance, I would like to wrap it around my app so I'm calling it at __layout.svelte:

// __layout.svelte

<script context="module">
	import apolloClient from '$lib/apolloClient';

	/**
	 * @type {import('@sveltejs/kit').Load}
	 */

	export async function load({ stuff }) {
		const client = apolloClient;
		return {
			stuff: {
				...stuff,
				client
			}
		};
	}
</script>

<slot />

You don't need the svelte-apollo package in order for this to work.

Lastly, you may query data in components/pages like so:

// someDataComponent.js
<script context="module">
	export async function load({ stuf  }) {
		return {
			props: {
				someData: await stuff.client.query({ query: SOME_DATA_QUERY })
			}
		};
	}
</script>

<script>
	import { SOME_DATA_QUERY } from '$lib/data-queries';

        export let someData
</script>

<section>
        <h1>{someData.heading}</h1>
        <p>{someData.paragraph}</p>
</section>

That's it!

Hope this helps!

tmrp avatar Oct 08 '21 19:10 tmrp

Another workaround not mentioned here is to add export map to @apollo/client package directly:

For me it was

  "exports":{
    ".": {
      "node":"./main.cjs", "default":"./index.js"
    },
    "./cache": {
      "node":"./cache/cache.cjs","default":"./cache/index.js"
    },
    "./core": {
      "node":"./core/core.cjs","default":"./core/index.js"
    },
    "./link/schema": {
      "node":"./link/schema/schema.cjs","default":"./link/schema/index.js"
    },
    "./link/context": {
      "node":"./link/context/context.cjs","default":"./link/context/index.js"
    },
    "./link/http": {
      "node":"./link/http/http.cjs","default":"./link/http/index.js"
    },
    "./link/ws": {
      "node":"./link/ws/ws.cjs","default":"./link/ws/index.js"
    }
  }

Make sure to delete all previous "solutions" and workaround as they would conflict. Track progress on issue here: https://github.com/apollographql/apollo-client/issues/8218

UPDATE: I also found out that rollup bundling crashes with

> [vite]: Rollup failed to resolve import "react" from "node_modules/.pnpm/@[email protected]_vc5lvukfq44ubwvv43scizgjye/node_modules/@apollo/client/react/context/ApolloConsumer.js".
This is most likely unintended because it can break your application at runtime.
If you do want to externalize this module explicitly add it to
`build.rollupOptions.external`

if you don't have compilerOptions.preserveValueImports set to false in your tsconfig

ZerdoX-x avatar Nov 24 '21 22:11 ZerdoX-x

@tmrp Hmm, did u exclude anything? I tried your answer but I'm still getting "Could not resolve 'react' (mark it as external to exclude it from the bundle)" with following:

import Apollo, * as ApolloScope from '@apollo/client/core/core.cjs';

const HttpLink = Apollo?.HttpLink || ApolloScope?.HttpLink;

const ApolloClient = Apollo?.ApolloClient || ApolloScope?.ApolloClient;

const InMemoryCache = Apollo?.InMemoryCache || ApolloScope?.InMemoryCache;

const link = new HttpLink({
	uri: process.env.VITE_GRAPHQL_ENDPOINT
});

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
	link,
	cache
});

export default apolloClient;

update: tried vite.optimizeDeps solution. 'react' problem was gone but got a new one with image

gran0123 avatar Jan 08 '22 14:01 gran0123

@tobiasgranlof updates to Svelte (presumably Vite) have prevented the method I mentioned from working.

I started a new SvelteKit project and got a similar error. It took me a while to rack my brain around it, but I got it working now.

To get it to work now you have to slightly restructure apolloClient.js like so:

import { HttpLink, InMemoryCache, ApolloClient } from '@apollo/client/core';

import { environmentVariables } from './environment-variables';

const link = new HttpLink({
	uri: environmentVariables.starWarsApi
});

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
	link,
	cache
});

export default apolloClient;

And you would have to add the following to your svelte.config.js file:

kit: {
 ...
  vite: {
    ssr: {
      noExternal: ['@apollo/client']
      }
    }
}

I made a working demo project you may checkout here

I hope this helps!

tmrp avatar Jan 08 '22 21:01 tmrp

Your example works but I guess because its plain js. Im using typescript and with the same configuration I do get the "react" problem when building. Even with same vite configuration as in your example. I tried both @apollo/client version 3.4.16 and latest. Same error. image

this is my apollo-client.ts:

import { HttpLink, InMemoryCache, ApolloClient } from '@apollo/client/core';

import { environmentVariables } from '$lib/environment-variables';

const link = new HttpLink({
	uri: environmentVariables.graphqlApi.toString()
});

const cache = new InMemoryCache();

const apolloClient = new ApolloClient({
	link,
	cache
});

export default apolloClient;

gran0123 avatar Jan 09 '22 09:01 gran0123

@tobiasgranlof, I think you made a mistake while trying to type check the environment variable here:

const link = new HttpLink({
  // you would want to give uri a string type here and not transform it to a string (because it's already a string)
  uri: environmentVariables.graphqlApi.toString()
  // replace the above with
   uri: environmentVariables.graphqlApi as string
});

To be safe, I changed my svelte.config.js to:




import adapter from '@sveltejs/adapter-auto';
import preprocess from 'svelte-preprocess';

/** @type {import('@sveltejs/kit').Config} */
const config = {
 // Consult https://github.com/sveltejs/svelte-preprocess
 // for more information about preprocessors
 preprocess: preprocess(),

 kit: {
  adapter: adapter(),

  // hydrate the <div id="svelte"> element in src/app.html
  target: '#svelte',
  vite: {
   optimizeDeps: {
    exclude: ['@apollo/client']
   },
   ssr: {
    noExternal: ['@apollo/client']
   }
  }
 }
};

export default config;


I made a TypeScript version, and you may check out the repo here here

I hope this helps!

Cheers!

tmrp avatar Jan 09 '22 10:01 tmrp

Yea, that worked. Not a fan of this setup but if it works, it works 😊 The only thing that is unclear, even tho we pass a cache to the apollo client is that it cant be reached server side.

const cacheResponse = apolloClient.readQuery({ query: getCarsQuery }); This code is exposed to another .ts file and this will always be null server side, since cache table only is available client side.

update: Tested with the client directly in the index.svelte file, my be server still gets request even with data in the cache. Tested with your ts example as well. Its calling the api constantly without checking the cache. Which means the cache is unecessary and useless to have.

gran0123 avatar Jan 09 '22 13:01 gran0123

it looks like the library was updated 10 days ago (and there is a new release), it now includes an example with sveltekit. however just testing, I get the following error.

Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '/Users/raphael/dev/nyt/node_modules/.pnpm/svelte-apo
[email protected]_0797cffbf2a19052134926e8d6fad18a/node_modules/@apollo/client/core' is not supported resolving ES
 modules imported from /Users/raphael/dev/nyt/node_modules/.pnpm/[email protected]_0797cffbf2a1905213492
6e8d6fad18a/node_modules/svelte-apollo/dist/svelte-apollo.js
Did you mean to import @[email protected][email protected]/node_modules/@apollo/client/core/core.cjs?
    at new NodeError (internal/errors.js:322:7)
    at finalizeResolution (internal/modules/esm/resolve.js:314:17)
    at moduleResolve (internal/modules/esm/resolve.js:776:10)
    at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:887:11)
    at Loader.resolve (internal/modules/esm/loader.js:89:40)
    at Loader.getModuleJob (internal/modules/esm/loader.js:242:28)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:76:40)
    at link (internal/modules/esm/module_job.js:75:36)

in case anyone else has seen and experienced this.

happysalada avatar Mar 03 '22 14:03 happysalada

@happysalada I got that as well (also reported here: https://github.com/timhall/svelte-apollo/issues/119)

Adding this to my svelte.config.js worked:

      optimizeDeps: {
        exclude: ['@apollo/client', 'svelte-apollo'],
      },
      ssr: {
        noExternal: ['@apollo/client', 'svelte-apollo'],
      },

under kit.vite

lorensr avatar May 22 '22 19:05 lorensr