Apollo-module: Example usage with Vue Composition API

Created on 7 Dec 2019  路  22Comments  路  Source: nuxt-community/apollo-module

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)
feature-request

Most helpful comment

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

All 22 comments

I will take a look at that.

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

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

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 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.

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.

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"

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_

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'

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

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);
  });
})

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 try @nuxtjs/composition-api - @0.21.0

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

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

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 馃憤

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

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

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. 馃憤 馃憤 馃憤

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']
},
Was this page helpful?
0 / 5 - 0 ratings