How to access '$apollo' outside vue component? - vue.js

How to make apollo accessible outside vue component.
I am verifying if the user exist and then allow the route to proceed further.
{
path: '/:username',
name: 'userProfilePage',
component: userProfilePage,
beforeEnter(routeTo, routeFrom, next) {
userExist(routeTo.params.username)
next()
}
Passing the username as a parameter to the userExist function.
import gql from "graphql-tag"
export default function userExist(username) {
this.$apollo
.query({
query: gql`
query($username: String!) {
login(username: $username) {
username
email
}
}
`,
variables: {
username: username
}
})
.then(res => {
console.log(res);
return res
})
.catch(err => {
console.log(err);
return err
});
}
But it is outputting the error:
Apollo client code
import Vue from 'vue'
import App from './App.vue'
import VueApollo from 'vue-apollo';
import { ApolloClient } from 'apollo-client'
import { HttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import router from './routes.js'
Vue.config.productionTip = false
const httpLink = new HttpLink({
uri: process.env.VUE_APP_DB_URL,
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache
})
Vue.use(VueApollo)
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
new Vue({
render: h => h(App),
router,
apolloProvider
}).$mount('#app')

So instead of initializing the apollo client in the App.vue file, initialize it in another file.
Something like clients.js, and export that client:
const httpLink = new HttpLink({
uri: process.env.VUE_APP_DB_URL,
})
const cache = new InMemoryCache()
export const apolloClient = new ApolloClient({
link: httpLink,
cache
})
Once done, import that in App.vue file like this:
import { apolloClient } from './clients.js';
Vue.use(VueApollo)
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
new Vue({
render: h => h(App),
router,
apolloProvider
}).$mount('#app')
Once done, import that client in any other file you want:
import { apolloClient } from './client.js';
import gql from "graphql-tag"
export default function userExist(username) {
apolloClient
.query({
query: gql`
query($username: String!) {
login(username: $username) {
username
email
}
}
`,
variables: {
username: username
}
})
.then(res => {
console.log(res);
return res
})
.catch(err => {
console.log(err);
return err
});
}

Related

How to set authentications headers with Vue apollo and composition api?

I've build my app with Vite. I read many documents on web about the topic but I'm still very confused. I've a login form that send credentials to a protected view. When post the data I set the headers and store the Bearer token in the local storage.
The problem is that it doesn't work cause the Bearer token result equal to null.
Only when I logout the token is set in the headers.
That's how is the header when I log in
And here how it's set when I log out...
My main.js code is this:
import { createApp, provide, h } from "vue";
import {
ApolloClient,
createHttpLink,
InMemoryCache,
} from "#apollo/client/core";
import { DefaultApolloClient } from "#vue/apollo-composable";
import App from "./App.vue";
import router from "./router";
import { createPinia } from "pinia";
import { provideApolloClient } from "#vue/apollo-composable";
const authToken = localStorage.getItem("auth-token");
const httpLink = createHttpLink({
uri: "http://localhost/graphql",
headers: {
Authorization: "Bearer " + authToken,
},
});
const cache = new InMemoryCache();
const apolloClient = new ApolloClient({
link: httpLink,
cache,
});
provideApolloClient(apolloClient);
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient);
},
render: () => h(App),
});
app
.use(router)
.use(createPinia())
.mount("#app");
and this is my routes.js
const router = createRouter({
history: createWebHistory(),
routes
})
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
const isAuthenticated = localStorage.getItem('auth-token');
if(requiresAuth && isAuthenticated===null){
next('/auth/login');
}else {
next();
}
});
I'm surely making some mistakes in my main.js but I cannot understand what's wrong. I'm very confused :-/
Thanks to who'll be able to help me.
Try using a helper function to get the token from local storage; I'm using this method and it's working fine for me. To get your code more organized, create a separate folder to define the apollo client. Here is the code:
// apolloClient.ts
import { ApolloClient, InMemoryCache, HttpLink } from "#apollo/client/core";
function getHeaders() {
const headers: { Authorization?: string; "Content-Type"?: string } = {};
const token = localStorage.getItem("access-token");
if (token) {
headers["Authorization"] = `Bearer ${token}`;
}
headers["Content-Type"] = "application/json";
return headers;
}
// Create an http link:
const httpLink = new HttpLink({
uri: `${import.meta.env.VITE_API_URL}/graphql`,
fetch: (uri: RequestInfo, options: RequestInit) => {
options.headers = getHeaders();
return fetch(uri, options);
},
});
// Create the apollo client
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
link: httpLink,
defaultOptions: {
query: {
errorPolicy: "all",
},
mutate: {
errorPolicy: "all",
},
},
});
Then you can use it in your main.ts like this:
// main.ts
import { createApp, h } from "vue";
import { provideApolloClient } from "#vue/apollo-composable";
import App from "./App.vue";
import { apolloClient } from "./apolloClient";
const app = createApp({
setup() {
provideApolloClient(apolloClient);
},
render: () => h(App),
});
app.mount("#app");

Vue2 Composition API Apollo client with id default not found

I have started having an issue with apollo client in vue2 when using inside a watch.
I have followed the setup guide for apollo client when using the composition api:
https://v4.apollo.vuejs.org/guide-composable/setup.html#_2-connect-apollo-client-to-vue
so my main.ts looks like this:
import contentfulClient from "./plugins/vue-apollo-contentful";
import apiClient from "./plugins/vue-apollo-api";
Vue.config.productionTip = false;
Vue.use(VueApollo);
new Vue({
router,
store,
vuetify,
setup() {
provide(ApolloClients, {
default: apiClient,
apiClient,
contentfulClient,
});
},
render: (h) => h(App),
}).$mount("#app");
The clients have their own files and are setup the same:
import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
const uri = `https://graphql.contentful.com/content/v1/spaces/${process.env.VUE_APP_CONTENTFUL_SPACE_ID}/environments/${process.env.VUE_APP_CONTENTFUL_ENV}?access_token=${process.env.VUE_APP_CONTENTFUL_ACCESS_TOKEN}`;
const link = createHttpLink({
uri,
});
const cache = new InMemoryCache();
const contentfulClient = new ApolloClient({
link,
cache,
});
export default contentfulClient;
I have this component:
import { defineComponent, onMounted, ref, watch } from "#vue/composition-api";
import { useGetCategory } from "#/logic/get-category";
export default defineComponent({
name: "Categories",
setup(_, context) {
const slug = ref(context.root.$route.params.slug);
const result = ref({});
const getCategory = (slug) => {
console.log(slug);
const { category, loading, error } = useGetCategory(slug);
result.value = { category, loading, error };
};
watch(() => context.root.$route.params.slug, getCategory);
onMounted(() => getCategory(slug.value));
return { result };
},
});
When this component loads, it "gets the category" by executing this:
import { useQuery, useResult } from "#vue/apollo-composable";
import * as getCategoryBySlug from "#/graphql/api/query.category.gql";
export function useGetCategory(slug: string) {
const { result, loading, error } = useQuery(getCategoryBySlug, { slug });
const category = useResult(result, null, (data) => data.getCategoryBySlug);
return { category, loading, error };
}
When the page loads, it gets the category fine, but if I change the route parameter (slug) I expect it to get the new category and display it. But instead I get this error:
So I figured that the setup is wrong in main.ts, so I added the non-composition-api aswell, found here:
https://apollo.vuejs.org/guide/installation.html#_1-apollo-client
Now my main.ts looks like this:
import contentfulClient from "./plugins/vue-apollo-contentful";
import apiClient from "./plugins/vue-apollo-api";
Vue.config.productionTip = false;
const apolloProvider = new VueApollo({
defaultClient: apiClient,
});
Vue.use(apolloProvider);
new Vue({
router,
store,
vuetify,
apolloProvider,
setup() {
provide(ApolloClients, {
default: apiClient,
apiClient,
contentfulClient,
});
},
render: (h) => h(App),
}).$mount("#app");
But this does not work. It compiles, but I still get the same error.
Does anyone know what I need to do to get this to work?
What works for me is
setup() {
provide(DefaultApolloClient, apolloClient);
},

vue-axios: Cannot read property 'post' of undefined

I try to send login data using axios and I get this error:
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property
'post' of undefined"
TypeError: Cannot read property 'post' of undefined
I used this.$http.post from documentation.
main.js
import Vue from "vue";
import App from "./App.vue";
import axios from "axios";
import VueAxios from "vue-axios";
import router from "./router/router";
import store from "./store/index";
import vuetify from "./plugins/vuetify";
Vue.config.productionTip = false;
Vue.use(VueAxios, axios);
new Vue({
router,
store,
vuetify,
render: h => h(App)
}).$mount("#app");
store/index.js
import Vue from "vue";
import Vuex from "vuex";
import account from "./account.module";
Vue.use(Vuex);
export default new Vuex.Store({
modules: {
account
}
});
stroe/account/module.js
import jwt_decode from "jwt-decode";
import accountService from "../services/account.service";
const token = localStorage.getItem("token");
const user = token ? jwt_decode(token) : null;
const state = token
? { loggedIn: true, user }
: { loggedIn: false, user };
const getters = {
}
const actions = {
login({ commit }, user) {
return accountService.login(user).then(
data => {
if (data.status == "success") {
const user = jwt_decode(data.token);
commit("loginSuccess", user);
} else {
commit("loginFailure");
}
return data;
});
}
}
const mutations = {
loginSuccess(state, user) {
state.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.loggedIn = false;
state.user = null;
},
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
services/account.service.js
const apiUrl = "***";
export default {
login(user) {
return this.$http.post(apiUrl + "login", {
login: user.login,
password: user.password
}).then(response => {
if (response.data.status == "success") {
localStorage.setItem("token", response.data.token);
}
return response.data;
});
}
}
VueAxios only creates a wrapper around the passed in axios, so this.$http.post() is the same as axios.post().
Thus, you could use axios directly in your services file:
import axios from 'axios'; 👈
const apiUrl = "***";
export default {
login(user) {
👇
return axios.post(apiUrl + "login", {
login: user.login,
password: user.password
}).then(/*...*/);
}
}

How to set authtoken in main.ts Vue3 vue/apollo-composable apollo/client

I am using vue3 add apollo-composable & apollo/client for graphql client.
This is my main.ts configure file.
import {createApp, provide, h} from 'vue'
import App from './App.vue'
import {ApolloClient, InMemoryCache} from "#apollo/client";
import {DefaultApolloClient} from '#vue/apollo-composable'
import router from './router'
import './assets/sass/main.sass'
const defaultclient = new ApolloClient({
uri: process.env.VUE_APP_API_HOST,
cache: new InMemoryCache(),
headers: {
"TOKEN": "....."
}
})
createApp({
setup() {
provide(DefaultApolloClient, defaultclient)
},
render() {
return h(App)
}
}).use(router).mount('#app')
I don't know how to change the token in the view/Home.vue
I can get graphqldata but I want to change header option before useResult ....
export default defineComponent({
components: {
UserListTable,
},
setup() {
const { result } = useQuery(usersQuery)
const users = useResult(result, null, (data: { userAccounts: any; }) => data.userAccounts)

apollo client , adding subscriptions while not breaking http link that uses jwt

I currently have a graphql api that handles HTTP requests, I've migrated to apollo-client and now I want to add subscriptions. The problem is, that I can't figure out how to combine my HTTP link (that has a JWT auth middleware).
I have no code errors to share, BUT, the issue is in the way i use the link split method. why? because when I remove it and just use authLink.concat(httpLink) everything works smoothly (except for that in this case I don't check if the connection is WS or HTTP...).
Here's my code:
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
// import ApolloClient from "apollo-boost"; // migrated to apollo-client
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
// subscriptions imports
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import VueApollo from "vue-apollo";
// links composition
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
});
const wsLink = new WebSocketLink({
uri: `ws://localhost:5000`,
options: {
reconnect: true
}
});
// this is the sketchy section
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
httpLink
)
// JWT middleware
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? token : ''
}
}
});
const wsPlusHttpWithJWT = authLink.concat(link)
export const apolloClient = new ApolloClient({
// link: authLink.concat(httpLink), // this worked
link: wsPlusHttpWithJWT,
cache: new InMemoryCache()
});
Vue.use(VueApollo);
const apolloProvider = new VueApollo({ defaultClient: apolloClient });
Vue.config.productionTip = false;
new Vue({
apolloProvider,
router,
render: h => h(App),
}).$mount("#app");