timhall / svelte-apollo

Svelte integration for Apollo GraphQL
MIT License
946 stars 67 forks source link

Usage With Svelte Kit #97

Open stolinski opened 3 years ago

stolinski commented 3 years ago
<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 @ 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)
moonmeister commented 3 years ago

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 commented 3 years ago

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.

unlocomqx commented 3 years ago

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.

bluwy commented 3 years ago

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"],
},
cschmatzler commented 3 years ago

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

moonmeister commented 3 years ago

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.

unlocomqx commented 3 years ago

@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

bluwy commented 3 years ago

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.

unlocomqx commented 3 years ago

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

draylegend commented 3 years ago

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"
    }
}
bluwy commented 3 years ago

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

bluwy commented 3 years ago

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.

tmrp commented 3 years ago

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!

ZerdoX-x commented 3 years ago

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/@apollo+client@3.6.6_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

gran0123 commented 2 years ago

@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
tmrp commented 2 years ago

@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!

gran0123 commented 2 years ago

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;
tmrp commented 2 years ago

@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!

gran0123 commented 2 years ago

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.

happysalada commented 2 years ago

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
llo@0.5.0_0797cffbf2a19052134926e8d6fad18a/node_modules/@apollo/client/core' is not supported resolving ES
 modules imported from /Users/raphael/dev/nyt/node_modules/.pnpm/svelte-apollo@0.5.0_0797cffbf2a1905213492
6e8d6fad18a/node_modules/svelte-apollo/dist/svelte-apollo.js
Did you mean to import @apollo+client@3.5.9_graphql@16.3.0/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.

lorensr commented 2 years ago

@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

ayandebnath commented 1 year ago

ref: https://github.com/timhall/svelte-apollo/issues/97#issuecomment-939058992

@tmrp Thanks, it worked!