How do I set up GraphQL for my vuejs 3 app - javascript

I am new to VueJS and I have been trying to setup graphql with my vuejs but I can't seem to get it right.
Funny thing is there's no error on my console apart from a
[Vue warn]: A plugin must either be a function or an object with an "install" function. error.
And this only comes up when I instantiate vueapollo to the use method.
Here's my main.js file
import { createApp } from 'vue';
import ElementUI from 'element-plus';
import ApolloClient from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import VueApollo from 'vue-apollo';
import App from './App.vue';
import router from './router';
import store from './store';
import './assets/style/theme/index.css';
// HTTP connection to the API
const httpLink = createHttpLink({
// You should use an absolute URL here
uri: 'http://localhost:4999/graphql',
});
// Cache implementation
const cache = new InMemoryCache();
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache,
});
/* The provider holds the Apollo client instances
that can then be used by all the child components. */
// eslint-disable-next-line
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
createApp(App)
.use(ElementUI)
.use(apolloClient)
.use(store)
.use(router)
.mount('#app');
I have a page file where presently, I want to reach the "Hello" endpoint I created earlier
<template>
<div>
<h1>Hello</h1>
<h1>{{ hello }}</h1>
</div>
</template>
<script>
import gql from 'graphql-tag';
export default {
data() {
return {
hello: '',
};
},
apollo: {
// Simple query that will update the 'hello' vue property
hello: gql`
query {
hello
}
`,
},
};
</script>

I also struggled with it a couple of weeks ago.
I don't remember how, but I got it to work.
I post my code here, in case it helps anyone in the future.
The difference between the question author and my code is how I pass the Apollo client to the create-app setup function. I don't know if it is correct, but it works.
PS: I am also using TypeScript on top of everything.
My apollo-client.ts file
import { ApolloError } from '#apollo/client';
import { ApolloClient, InMemoryCache, HttpLink } from '#apollo/client/core';
import { ErrorResponse } from '#apollo/client/link/error';
import { onError } from '#apollo/client/link/error';
import { logErrorMessages } from '#vue/apollo-util';
function getHeaders() {
const headers = {
'content-type': 'application/json',
};
/* const token = window.localStorage.getItem('apollo-token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
} */
return headers;
}
// Create an http link:
const httpLink = new HttpLink({
uri: 'http://localhost:3000/query',
fetch: (uri: RequestInfo, options: RequestInit) => {
options.headers = getHeaders();
return fetch(uri, options);
},
});
const errorLink = onError((error: ErrorResponse | ApolloError) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error);
}
});
// Create the apollo client
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
connectToDevTools: true,
link: errorLink.concat(httpLink),
});
My main.ts file:
import { apolloClient } from './apollo-client';
import { createApp, provide, h } from 'vue';
import { DefaultApolloClient } from '#vue/apollo-composable';
import { createApolloProvider } from '#vue/apollo-option';
import { loadFonts } from './plugins/webfontloader';
import App from './App.vue';
import router from './router';
import store from './store';
import vuetify from './plugins/vuetify';
// TODO: Auth
// import authPlugin from "./auth/authPlugin"
// Create a provider
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
});
loadFonts();
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient);
},
render: () => h(App),
});
app.use(router).use(store).use(vuetify).use(apolloProvider).mount('#app');

Related

SyntaxError: Named export 'ApolloClient' not found

I'm trying to create server with ApolloClient and GraphQL but got the following error:
SyntaxError: Named export 'ApolloClient' not found. The requested
module '#apollo/client' is a CommonJS module, which may not support
all module.exports as named exports.
my code looks like this:
import { ApolloClient, InMemoryCache, createHttpLink } from '#apollo/client'
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
})
const createApolloClient = () => {
return new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
})
}
export default createApolloClient
I tried
import pkg from '#apollo/client'
const { ApolloClient, InMemoryCache, createHttpLink } = pkg
but it didn't help
Good news #davit-gelovani
I reached to import Apollo the way you need, just like that :
import { ApolloClient, InMemoryCache } from "#apollo/client/core/core.cjs";
import { HttpLink } from "#apollo/client/link/http/http.cjs";
on #apollo/client version 3.7.0 🚀

[Vue warn]: Computed property "axios" is already defined in Data. at <App>

I know similar questions already present in stackoverflow, but I still can't understand how to solve this. I am having the warning(look in the title) in my console.
You can reproduce the warning by the following code
//index.js
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
import axios from 'axios';
const app = createApp(App)
app.__proto__.axios = axios
app.use(store)
app.mount("#app")
##App.vue
<template>
<div class="TodoList">
<p v-for="todo in todos" :key="todo.id">{{ todo.title }}</p>
</div>
</template>
<script>
export default {
mounted() {
this.$store.dispatch("fillItems");
},
computed: {
todos() {
return this.$store.getters.todos;
},
},
};
</script>
<style>
</style>
##store.js
import { createStore } from 'vuex';
export const store = createStore({
state: {
todos: []
},
getters: {
todos(state) {
return state.todos
}
},
mutations: {
FILL_ITEMS(state, payload) {
state.todos = payload
}
},
actions: {
fillItems({ commit }) {
this.axios
.get("https://jsonplaceholder.typicode.com/todos")
.then(res => commit('FILL_ITEMS', res.data))
}
}
})
You could add axios to app.config.globalProperties in order to access it inside any child component :
const app = createApp(App)
app.config.globalProperties.axios=axios
in child component use this.axios
but you couldn't access it inside the store context because this in the actions refers to the store instance, so you should import axios inside the store file and use it like :
import { createStore } from 'vuex';
import axios from 'axios';
export const store = createStore({
state: {
todos: []
},
getters: {
todos(state) {
return state.todos
}
},
mutations: {
FILL_ITEMS(state, payload) {
state.todos = payload
}
},
actions: {
fillItems({ commit }) {
axios
.get("https://jsonplaceholder.typicode.com/todos")
.then(res => commit('FILL_ITEMS', res.data))
}
}
})
or you could assign axios to the store instance (It's not recommended specially with typescript) :
const app = createApp(App)
store.axios = axios
app.use(store)
app.mount("#app")
In Vue 3, you can create app globals for components using provide/inject:
Providing
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
import axios from 'axios';
const app = createApp(App)
app.provide('axios', axios); // Providing to all components here
app.use(store)
app.mount("#app")
Injecting
In the options API:
export default {
inject: ['axios']; // injecting in a component that wants it
}
In the composition API:
const { inject } = Vue;
...
setup() {
const axios = inject('axios'); // injecting in a component that wants it
}
Edit:
I answered too fast (thanks #BoussadjraBrahim), you're not asking about components, but I'll leave that answer too. If you just want to use axios in a separate module, you can use it like any import:
import axios from 'axios';
and use axios instead of this.axios

How to add #vue/apollo-composable to Quasar Frramework

We're trying to add a boot file to Quasar Framwework to be able to use #vue/apollo-composable with the Vue composition API. This tutorial explains how that is done for the old apollo-client and this one for the new version.
The issue we're having is to connect the Apollo client to Vue. So we need to translate the example from the docs to a Quasar boot file:
// example docs
import { provide } from '#vue/composition-api'
import { DefaultApolloClient } from '#vue/apollo-composable'
const app = new Vue({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: h => h(App),
})
The Quasar boot file:
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache
});
export default async ({ app } /* { app, router, Vue ... } */) => {
app.setup(provide(DefaultApolloClient, apolloClient))
}
The issue:
What is the correct syntax to use in the Quasar Framework boot file to add the Apollo client?
Found the correct syntax in this answer:
import { boot } from 'quasar/wrappers'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
import config from 'src/app-config.json'
export default boot(({ app }) => {
const httpLink = createHttpLink({
uri: config.resources.gatewayApi.uri,
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
app.setup = () => {
provide(DefaultApolloClient, apolloClient)
return {}
}
})
I translated #DarkLite1's code to be TypeScript compatible. So following is the file src/boot/vue-apollo-4.ts (don't forget to register it in quasar.conf.js):
<pre>
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
import { boot } from 'quasar/wrappers'
const httpLink = createHttpLink({
uri: 'http://localhost:8080/v1/graphql'
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache
})
export default boot(({ app }) => {
app.setup = () => {
provide(DefaultApolloClient, apolloClient)
return {}
}
})
</pre>
try use this :
export default ({ app, Vue }) => {
Vue.use(VueApollo)
app.apolloProvider = apolloProvider
}

Vuex store is not accessible in external js files

The architecture of my app is attached here
Architecture of my app
So in my use case I just want to use getters in Axios interceptor (/../src/app/shared/services/http-client/http-client.js)so I can attach the authorization token in header but when I import store from /../src/app/app-state.js then it throws error as follow
Uncaught TypeError: Cannot read property 'getters' of undefined
at eval (vuex.esm.js?2f62:340)
at Array.forEach (<anonymous>)
at assertRawModule (vuex.esm.js?2f62:339)
at ModuleCollection.register (vuex.esm.js?2f62:244)
at eval (vuex.esm.js?2f62:258)
at eval (vuex.esm.js?2f62:123)
at Array.forEach (<anonymous>)
at forEachValue (vuex.esm.js?2f62:123)
at ModuleCollection.register (vuex.esm.js?2f62:257)
at new ModuleCollection (vuex.esm.js?2f62:218)
app-state.js (vuex store) /../src/app/app-state.js
import Vue from 'vue';
import Vuex from 'vuex';
import createPersistedState from "vuex-persistedstate";
import { appName } from '../environment/environment'
import { authState } from './auth'
import { organizationState } from "./organization"
Vue.use(Vuex);
export default new Vuex.Store({
state: {},
mutations: {},
actions: {},
modules: {
authState,
organizationState
},
strict:false,
plugins: [
createPersistedState ({
key: appName,
})
]
});
main.js
import Vue from 'vue';
import './plugins';
import i18n from './plugins/i18n'
import './plugins/izitoast'
import App from './app/app.vue';
import DEFINES from './plugins/defines'
import './main.scss';
import router from './app/app-routes';
import store from './app/app-state';
import vuetify from './plugins/vuetify';
Vue.config.productionTip = false;
Vue.prototype.DEFINES = DEFINES;
new Vue({
router,
store,
vuetify,
i18n,
render: h => h(App)
}).$mount('#app');
**Organization module state (example of a module) **
import { list } from '../services'
import { getField, updateField } from 'vuex-map-fields';
/** Initial State */
const initialState = {
loading: false,
data: null,
error: null
}
/**
* Organization data mutations
*/
const mutations = {
updateField,
/** Organization data request */
ORGANIZATION_DATA_REQUEST(state,payload) {
Object.assign(state, {loading: true, data: payload})
},
/** Organization data success */
ORGANIZATION_DATA_SUCCESS(state, payload) {
Object.assign(state, {loading: false, data: payload})
},
/** Organization data error */
ORGANIZATION_DATA_ERROR(state) {
Object.assign(state, {
loading: false,
});
},
/** Organization data reset */
ORGANIZATION_DATA_RESET(state) {
Object.assign(state,...initialState)
}
}
const actions = {
async list(context){
// 1. Initiate request
context.commit('ORGANIZATION_DATA_REQUEST');
// 2. Get data from API and handle error
var response = await list().catch(error => {
context.commit('ORGANIZATION_DATA_ERROR')
throw error;
})
// 3. Set data in state
context.commit('ORGANIZATION_DATA_SUCCESS', response)
return response
}
}
const getters ={
getField,
getList: (state) => {
return state.data
},
}
export default {
namespaced: true,
mutations,
actions,
getters,
state: initialState
}
orjanization-state.js which combines all the feature states
import { organizationList } from "./shared/state"
export default {
modules: {
organizationList
}
}
well, if you want to get access to Vuex modules in a js file you can import vuex store and use it,
here is my code you can see I'm using Vuex actions in Axios:
import Store from "#/store/index";
import axios from "axios";
const userRole = localStorage.role ? `${localStorage.role}` : "";
let config = {
baseURL: process.env.VUE_APP_BASE_URL + userRole,
headers: {
Authorization: "Bearer " + localStorage.token
},
};
const _axios = axios.create(config);
// Add a response interceptor
_axios.interceptors.request.use(
function(config) {
Store.dispatch("loader/add", config.url);
return config;
},
function(error) {
Store.dispatch("loader/remove", error.response.config.url);
return Promise.reject(error);
}
);
_axios.interceptors.response.use(
function(response) {
// Do something with response data
Store.dispatch("loader/remove", response.config.url);
return response;
},
function(error) {
Store.dispatch("loader/remove", error.response.config.url);
return Promise.reject(error);
}
);
export const $http = _axios;
I hope this code will help you

Meteor/SSR/Apollo client - Trying to set up SSR with Apollo and getting fetch is not found gloablly

I am trying to set up Server Side Rendering with my meteor app that uses Apollo. I am getting the following error which I do not manage to solve (even following various post online -github and SO - and trying with different packages : unfetch, node-fetch...)
Error running template: Invariant Violation: fetch is not found
globally and no fetcher passed, to fix pass a fetch for your
environment like https://www.npmjs.com/package/node-fetch.
For example: import fetch from 'node-fetch'; import { createHttpLink }
from 'apollo-link-http';
Here is the code on the server side:
import fetch from 'node-fetch';
import { 
Accounts
} from 'meteor/accounts-base';
import { 
Meteor
} from 'meteor/meteor';
import {
onPageLoad
} from 'meteor/server-render';
import { 
StaticRouter
} from 'react-router-dom';
import {
renderToString
} from 'react-dom/server';
import React from 'react';
import { 
routes
} from './../both/router';
import Menu from './../../ui/Menu';
import {
ApolloProvider
} from 'react-apollo';
import {
ApolloClient
} from 'apollo-client';
import {
createHttpLink
} from 'apollo-link-http'
import './apollo'; // THIS IS THE APOLLO SERVER CODE
onPageLoad((sink) => {
let App = props => ( <StaticRouter location = {
props.location
} > {
routes
} </StaticRouter>
)
const client = new ApolloClient({
ssrMode: true,
link: createHttpLink({
uri: '/graphql',
credentials: 'same-origin',
headers: {
cookie: sink.request.cookies,
},
}),
fetch: fetch,
cache: new InMemoryCache(),
});
let AppToRender = props => ( <ApolloProvider client={
client
}>
<App />
</ApolloProvider>
)
sink.renderIntoElementById('app', renderToString( <AppToRender location={
sink.request.url
}
/>));
});
I also tried Apollo-Client from apollo-boost and it didn't work either. Would very much appreciate if someone could help with the issue.
After reinstalling and removing three more times unfetch and node-fetch and importing them everywhere I found out that during my test I once also moved the "fetch: fetch" outside of the createHttpLink but still inside the new Apollo-Client on client side.
Finally it works with the following imports :
import fetch from 'unfetch';
import React from 'react';
import { Accounts } from 'meteor/accounts-base';
import { Meteor } from 'meteor/meteor';
import { onPageLoad } from 'meteor/server-render';
import { StaticRouter } from 'react-router-dom';
import { renderToString } from 'react-dom/server';
import { routes } from './../both/router';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import Menu from './../../ui/Menu';
import './email-templates';
import './apollo';
import './users';
and the following creation of the Apollo Client on server side for SSR :
onPageLoad((sink) => {
let App = props => (
<StaticRouter location={props.location}>
{routes}
</StaticRouter>
)
const client = new ApolloClient({
ssrMode: true,
link: createHttpLink({
uri: '/graphql',
credentials: 'same-origin',
headers: {
cookie: sink.request.cookies,
},
fetch: fetch,
}),
cache: new InMemoryCache(),
});
let AppToRender = props => (
<ApolloProvider client={client}>
<Menu />
<App />
</ApolloProvider>
)
sink.renderIntoElementById('app', renderToString(<AppToRender location={sink.request.url} />));
});
Have to admit the indentation on S.O. didn't show well which was harder to solve my issue but it was fine in my project. I tried so many different things that I forgot to go back from start in the creation of the Apollo Client.

Categories