apollo icon indicating copy to clipboard operation
apollo copied to clipboard

Example usage with Vue Composition API

Open lewebsimple opened this issue 4 years ago • 25 comments

What problem does this feature solve?

Vue-Apollo 4.x.x alpha release is based on the new Vue Composition API. Would be great to be able to use it with @nuxtjs/apollo

This feature request is available on Nuxt community (#c267)

lewebsimple avatar Dec 07 '19 12:12 lewebsimple

I will take a look at that.

kieusonlam avatar Dec 09 '19 01:12 kieusonlam

Any progress on this? Tried the apollo module with the new useQuery and get a Error: Apollo Client with id default not found

Linus-Boehm avatar Jan 03 '20 10:01 Linus-Boehm

By the way, is there a workaround to use @nuxtjs/apollo in a nuxt project which uses composition API with @vue/composition-api?

ozum avatar Jan 16 '20 15:01 ozum

I found an option how to use @vue/apollo-composable. Need to perform a couple of simple steps: Install @vue/apollo-composable, create plugin provide-apollo-client.ts (name can be any) with the following content:

// plugins/provide-apollo-client.js
import { provide } from '@vue/composition-api';
import { DefaultApolloClient } from '@vue/apollo-composable';

export default ({ app }) => {
	app.setup = () => {
		const apolloClient = app.apolloProvider.defaultClient;

		provide(DefaultApolloClient, apolloClient);
	}
}

Specify it in the config with the main API

// nuxt.config.js
plugins: [
	'@/plugins/composition-api',
	'@/plugins/provide-apollo-client'
],

The rest of the configuration is no different. However, there are still unsolved problems with SSR errors (example: 404 status code).

negezor avatar Feb 03 '20 05:02 negezor

@negezor I think assigning app.setup = () => { ... } could be a problem if more than one Nuxt plugin want to customize app.setup ... i.e. this is not composable in itself and raises a TS error Type '() => void' is not assignable to type 'SetupFunction<Data, Data>'.

Maybe injecting apolloClient the usual "nuxt way" could work ... I also asked a related question on Discord.

This points to a missing feature of Nuxt for making plugins work nicely with the Composition API.

lewebsimple avatar Apr 25 '20 15:04 lewebsimple

Following @negezor's plugin, (@lewebsimple and in a more nuxy way via nuxt-composition-api):

// ~/plugins/apollo/provide.ts
import { provide, reactive } from "nuxt-composition-api";
import { Context, Plugin } from "@nuxt/types";
import { ApolloClients } from "@vue/apollo-composable";

const provideApollo: Plugin = ({ app }: Context) => {
  app.setup = () => {
    // since plugin is eval'd after module app.apolloProvider will always be defined
    const clients = reactive(app.apolloProvider?.clients); 
    provide(DefaultApolloClient, clients?.defaultClient);
    // not functionally important but req'd return type
    return { [ApolloClients]: clients }; 
  };
};

export default provideApollo;

One important thing to note is that @vue/apollo-composable expects a config named default, whereas @nuxtjs/apollo's default config is named defaultClient. If you provide ApolloClients vs. DefaultApolloClient, you must use { clientId: "defaultConfig" } as an option when calling apollo-composeable.

<template>
  {{ result.company.name }}
</template>
<script lang="ts">
  import { Component, Vue } from "nuxt-property-decorator";
  import "vue-apollo"; // just noticed this is here, don't recall why...
  import { useCompanyQuery } from "@/generated/graphql-codegen";

  @Component({
    setup: () => {
      const { result, loading, error } = useCompanyQuery({
        where: { isParent: true }
      });

      return { result, loading, error };
    }
  })
  export default class CompaniesCard extends Vue {}
</script>

NB: Companies and Company are different queries.

~For fluency's sake defaultClient needs to be renamed default for interop. with @vue/apollo-composable.~

Edit: Weirdly enough vue-apollo does use defaultClient, and @vue/apollo-composeable uses a different property name.

kazazes avatar Jun 09 '20 20:06 kazazes

Hi,

I'm pretty sure I made a mistake somewhere ... I have this error displayed:

const useFetch = (callback) => {
    var _a;
    const vm = getCurrentInstance();
    if (!vm)
        throw new Error('This must be called within a setup function.');
    registerCallback(vm, callback);
    if (typeof vm.$options.fetchOnServer === 'function') {
        vm._fetchOnServer = vm.$options.fetchOnServer.call(vm) !== false;
    }

nuxt.config.js

  buildModules: [
    '@nuxt/typescript-build',
    'nuxt-composition-api',
  ],
  modules: [
    '@nuxtjs/apollo',
  ],
  plugins: [
    '~/plugins/provideApolloClient.ts',
  ],
  apollo: {
 ...

./plugins/provideApolloClient.ts

import { provide, reactive } from 'nuxt-composition-api';
import { Context, Plugin } from '@nuxt/types';
import { DefaultApolloClient } from '@vue/apollo-composable';

const provideApollo: Plugin = ({ app }: Context) => {
  app.setup = () => {
    // since plugin is eval'd after module app.apolloProvider will always be defined
    const clients = reactive(app.apolloProvider?.clients);
    provide(DefaultApolloClient, clients?.defaultClient);
    // not functionally important but req'd return type
    return { [DefaultApolloClient]: clients };
  };
};

export default provideApollo;

index.vue

import 'vue-apollo'; // just noticed this is here, don't recall why...

package.json

  "dependencies": {
    "@nuxt/types": "^2.13.3",
    "@vue/apollo-composable": "^4.0.0-alpha.8",
    "graphql-tag": "^2.10.4",
    "nuxt-composition-api": "^0.10.6",
  },
  "devDependencies": {
    "@nuxtjs/apollo": "^4.0.1-rc.1",

The complet projext: https://github.com/shenron/vue-nuxt-tsx

Has someone a clue? Thank you very much.

Edit: Now I don't have errors, just a the page is in pending. The same query works with @nuxt/apollo without composition. Edit 2: The last version of nuxt-composition-api works with this is: "nuxt-composition-api": "^0.8.2"

tcastelly avatar Jul 20 '20 19:07 tcastelly

There's an update to nuxt-composition-api that provides an onGlobalSetup hook. So now the plugin can just be (typescript)

import { Context } from "@nuxt/types";
import { provide, onGlobalSetup, defineNuxtPlugin } from "nuxtjs/composition-api";
import { DefaultApolloClient } from "@vue/apollo-composable";

/**
 * This plugin will connect @nuxt/apollojs with @vue/apollo-composable
 */
export default defineNuxtPlugin({ app }: Context): void => {
  onGlobalSetup(() => {
    provide(DefaultApolloClient, app.apolloProvider?.defaultClient);
  });
});

and then of course add the plugin

plugins: ["@/plugins/apollo/plugin.ts"]

edit: use @nuxtjs/composition-api and defineNuxtPlugin

NickBolles avatar Aug 05 '20 02:08 NickBolles

Just a note from @NickBolles response (which worked perfect for me), the nuxt composition api library is now @nuxtjs/composition-api' so it should be:

import { provide, onGlobalSetup } from '@nuxtjs/composition-api'

toddheslin avatar Aug 26 '20 05:08 toddheslin

Thanks @toddheslin! I've updated the example above to use the new @nuxtjs/composition-api and the defineNuxtPlugin helper too.

NickBolles avatar Aug 26 '20 12:08 NickBolles

Fixed up example

import { Context } from '@nuxt/types';
import { provide, onGlobalSetup, defineNuxtPlugin } from "@nuxtjs/composition-api";
import { DefaultApolloClient } from "@vue/apollo-composable";

/**
 * This plugin will connect @nuxt/apollojs with @vue/apollo-composable
 */

export default defineNuxtPlugin(({ app }: Context): void => {
  onGlobalSetup(() => {
    provide(DefaultApolloClient, app.apolloProvider?.defaultClient);
  });
})

manniL avatar Oct 21 '20 09:10 manniL

Fixed up example

import { Context } from '@nuxt/types';
import { provide, onGlobalSetup, defineNuxtPlugin } from "@nuxtjs/composition-api";
import { DefaultApolloClient } from "@vue/apollo-composable";

/**
 * This plugin will connect @nuxt/apollojs with @vue/apollo-composable
 */

export default defineNuxtPlugin(({ app }: Context): void => {
  onGlobalSetup(() => {
    provide(DefaultApolloClient, app.apolloProvider?.defaultClient);
  });
})

My project has an error, please tell me how to solve this problem

Cannot read property 'DefaultApolloClient' of undefined

nuxt - 2.15.3 @nuxtjs/apollo - 4.0.1-rc.5 @nuxtjs/composition-api - 0.22.1 @vue/apollo-composable - 4.0.0-alpha.12

Console error ERROR [Vue warn]: Error in data(): "TypeError: Cannot read property 'DefaultApolloClient' of undefined"

Rasool-deldar avatar Mar 22 '21 06:03 Rasool-deldar

@Rasool-deldar try @nuxtjs/composition-api - @0.21.0

toddheslin avatar Mar 22 '21 08:03 toddheslin

@Rasool-deldar try @nuxtjs/composition-api - @0.21.0

install @nuxtjs/composition-api - 0.21.0 & 0.20.0

It still shows this error - Cannot read property 'DefaultApolloClient' of undefined

link test ( display error ) - https://codesandbox.io/s/elastic-satoshi-otjer

Rasool-deldar avatar Mar 22 '21 12:03 Rasool-deldar

@Rasool-deldar That link wasn't working but here is what I'm currently using:

"@nuxtjs/apollo": "^4.0.1-rc.5",
"@nuxtjs/composition-api": "^0.21.0",
"@vue/apollo-composable": "^4.0.0-alpha.12"

Now I remember what I had to change to get it to this point...

  1. transpile in nuxt.config.js
/*
  ** Build configuration
  */
build: {
  transpile: ['@vue/apollo-composable'],    
},

Then, replace all references to import like this (from /dist):

import { DefaultApolloClient } from '@vue/apollo-composable/dist'

That's what I needed to do to move from an earlier version (I've been using it for about a year) till the latest ones)

Hope this helps!

toddheslin avatar Mar 22 '21 20:03 toddheslin

dist

import { DefaultApolloClient } from '@vue/apollo-composable/dist'

Thank you for your help.

I have used this method

import { DefaultApolloClient } from '@vue/apollo-composable'

I have replaced it with the following method

import { DefaultApolloClient } from '@vue/apollo-composable/dist'

I tested it on this version, it worked 0.21.0 & 0.22.3 @nuxtjs/apollo": "^4.0.1-rc.5", "@nuxtjs/composition-api": "^0.21.0", "@vue/apollo-composable": "^4.0.0-alpha.12"

Rasool-deldar avatar Mar 23 '21 06:03 Rasool-deldar

@Rasool-deldar That link wasn't working but here is what I'm currently using:

"@nuxtjs/apollo": "^4.0.1-rc.5",
"@nuxtjs/composition-api": "^0.21.0",
"@vue/apollo-composable": "^4.0.0-alpha.12"

Now I remember what I had to change to get it to this point...

  1. transpile in nuxt.config.js
/*
  ** Build configuration
  */
build: {
  transpile: ['@vue/apollo-composable'],    
},

Then, replace all references to import like this (from /dist):

import { DefaultApolloClient } from '@vue/apollo-composable/dist'

That's what I needed to do to move from an earlier version (I've been using it for about a year) till the latest ones)

Hope this helps!

how use ssr ? .

not work ssr useQuery( Test, {}, { prefetch: true, } );

https://codesandbox.io/s/falling-glitter-t4ko2?file=/pages/index.vue:848-938

Rasool-deldar avatar Mar 23 '21 13:03 Rasool-deldar

SSR should just work out of the box. When the page is loaded from the server, the GQL request is made server-side, otherwise it's client.

Not sure what issue you're facing here but glad to know the config changes got the upgrade to work 👍

toddheslin avatar Mar 23 '21 20:03 toddheslin

SSR should just work out of the box. When the page is loaded from the server, the GQL request is made server-side, otherwise it's client.

Not sure what issue you're facing here but glad to know the config changes got the upgrade to work 👍

I do not know exactly how to handle ssr. Without waiting for the data to go to the server, it goes to the data client, and there it says, please help me, how can I solve this problem? ( localhost - npm run dev )

Look at this link below in the terminal and console, you will see the undefined value on the server side, then the data value will return to the user side.

nuxt-mode ssr: true,

https://codesandbox.io/s/falling-glitter-t4ko2?file=/pages/index.vue:848-938

Rasool-deldar avatar Mar 24 '21 17:03 Rasool-deldar

OK I see your problem here. You can't use async functions in a vue 2.x setup function (ie current version of nuxt). I'll add some commentary:

const movies = ref([]);

// No point doing async/await here because the callback function is called after data is resolved
onResult(async ({ data }) => {
    movies.value = await data.movies;
});

// this will always be undefined because we aren't blocking the component from rendering above (and can't)
console.log(result.value);

// same as above
console.log(movies.value);

// setup() is called on both server and client, so it's a bit misleading to log 'ssr' here
console.log("ssr");

The correct way to do it is this:

<script>
import { defineComponent } from "@nuxtjs/composition-api";
import { useQuery, useResult } from "@vue/apollo-composable/dist";

export default defineComponent({
  components: {
    Logo,
    IconLink,
  },
  setup() {
    // not work ssr
    const { result, loading, error } = useQuery(
      Test,
      {},
      {
        fetchPolicy: "no-cache",
        prefetch: true,
      }
    );
    const movies = useResult(result)
    
    return {
      movies,
      error,
      loading,
    };
  },
});
</script>

In your <template> you can render out movies if we aren't loading or if there is no error. The only time you need to really use onResult() (which I do use a lot) is where you are setting module-scoped refs from a composable that need to be shared state between different components. This is more of an advanced case when you're feeling comfortable with the above.

Check out useResult here: https://v4.apollo.vuejs.org/guide-composable/query.html#useresult

toddheslin avatar Mar 24 '21 20:03 toddheslin

SSR should just work out of the box. When the page is loaded from the server, the GQL request is made server-side, otherwise it's client. Not sure what issue you're facing here but glad to know the config changes got the upgrade to work 👍

I do not know exactly how to handle ssr. Without waiting for the data to go to the server, it goes to the data client, and there it says, please help me, how can I solve this problem? ( localhost - npm run dev )

Look at this link below in the terminal and console, you will see the undefined value on the server side, then the data value will return to the user side.

nuxt-mode ssr: true,

https://codesandbox.io/s/falling-glitter-t4ko2?file=/pages/index.vue:848-938 Thank you very much for your help

OK I see your problem here. You can't use async functions in a vue 2.x setup function (ie current version of nuxt). I'll add some commentary:

const movies = ref([]);

// No point doing async/await here because the callback function is called after data is resolved
onResult(async ({ data }) => {
    movies.value = await data.movies;
});

// this will always be undefined because we aren't blocking the component from rendering above (and can't)
console.log(result.value);

// same as above
console.log(movies.value);

// setup() is called on both server and client, so it's a bit misleading to log 'ssr' here
console.log("ssr");

The correct way to do it is this:

<script>
import { defineComponent } from "@nuxtjs/composition-api";
import { useQuery, useResult } from "@vue/apollo-composable/dist";

export default defineComponent({
  components: {
    Logo,
    IconLink,
  },
  setup() {
    // not work ssr
    const { result, loading, error } = useQuery(
      Test,
      {},
      {
        fetchPolicy: "no-cache",
        prefetch: true,
      }
    );
    const movies = useResult(result)
    
    return {
      movies,
      error,
      loading,
    };
  },
});
</script>

In your <template> you can render out movies if we aren't loading or if there is no error. The only time you need to really use onResult() (which I do use a lot) is where you are setting module-scoped refs from a composable that need to be shared state between different components. This is more of an advanced case when you're feeling comfortable with the above.

Check out useResult here: https://v4.apollo.vuejs.org/guide-composable/query.html#useresult

Thank you very much for your help. 👍 👍 👍

Rasool-deldar avatar Mar 26 '21 07:03 Rasool-deldar

Anybody experienced the error of TypeError: defaultClientConfig is undefined here is my plugin:

import {Context} from '@nuxt/types'
import {
  provide,
  onGlobalSetup,
  defineNuxtPlugin
} from '@nuxtjs/composition-api'
import {DefaultApolloClient} from '@vue/apollo-composable/dist'

/**
 * This plugin will connect @nuxt/apollojs with @vue/apollo-composable
 */

export default defineNuxtPlugin(({app}: Context): void => {
  onGlobalSetup(() => {
    provide(DefaultApolloClient, app.apolloProvider?.defaultClient)
  })
})

I also added the transpile configuration on my nuxt.config.js

build: {
  ...
  transpile: ['@vue/apollo-composable']
},

jcjp avatar May 07 '21 07:05 jcjp

I have tried setting it up as described by others in this issue. However, I am currently having trouble with the Nuxt client throwing the following error: "Apollo client with id default not found. Use provideApolloClient() if you are outside of a component setup."

Initially it renders the page correctly with the GraphQL query being executed correctly on the server, but it seems to fail after it gets hydrated. Here is a repository to reproduce the issue: https://github.com/Tomaszal/nuxt-apollo-composable-test

After some debugging, it seems that ~~this resolveClient function is being called with an undefined value~~ injecting ApolloClients and DefaultApolloClient returns null on the Nuxt client, but I cannot figure out why. Perhaps someone knows better why this is happening?

EDIT: Was a silly mistake. I copied the code for the plugin with DefaultApolloClient being imported from '@vue/apollo-composable', but I imported the composition functions from '@vue/apollo-composable/dist' instead. If anyone has the same issue make sure to import everything from the same place, as DefaultApolloClient is a unique symbol. Not sure if the '/dist' part is still necessary, for me it works without it.

Tomaszal avatar Aug 18 '21 15:08 Tomaszal

I have tried setting it up as described by others in this issue. However, I am currently having trouble with the Nuxt client throwing the following error: "Apollo client with id default not found. Use provideApolloClient() if you are outside of a component setup."

Initially it renders the page correctly with the GraphQL query being executed correctly on the server, but it seems to fail after it gets hydrated. Here is a repository to reproduce the issue: https://github.com/Tomaszal/nuxt-apollo-composable-test

After some debugging, it seems that ~this resolveClient function is being called with an undefined value~ injecting ApolloClients and DefaultApolloClient returns null on the Nuxt client, but I cannot figure out why. Perhaps someone knows better why this is happening?

EDIT: Was a silly mistake. I copied the code for the plugin with DefaultApolloClient being imported from '@vue/apollo-composable', but I imported the composition functions from '@vue/apollo-composable/dist' instead. If anyone has the same issue make sure to import everything from the same place, as DefaultApolloClient is a unique symbol. Not sure if the '/dist' part is still necessary, for me it works without it.

import { Context } from '@nuxt/types'
import {
  onGlobalSetup,
  defineNuxtPlugin
} from '@nuxtjs/composition-api'
// @ts-ignore
import { provideApolloClient } from '@vue/apollo-composable'

/**
 * This plugin will connect @nuxt/apollojs with @vue/apollo-composable
 */

export default defineNuxtPlugin(({ app }: Context): void => {
  onGlobalSetup(() => {
    provideApolloClient(app.apolloProvider?.defaultClient)
  })
})

Davidnadejdin avatar Oct 07 '21 07:10 Davidnadejdin

With Nuxt 2 Bridge this is how I defined the provider, see: https://stackoverflow.com/q/73986807/2771889

import { provideApolloClient } from '@vue/apollo-composable'

export default defineNuxtPlugin((nuxtApp) => {
  provideApolloClient(nuxtApp.apolloProvider?.defaultClient)
})

thisismydesign avatar Oct 07 '22 11:10 thisismydesign