apolloClient aborted after graphql request - vue.js

I'm trying to make a request to graphql api and it's aborting me after I added no-cors. Before this it was giving me this error Access to fetch at 'https://venia.magento.com/graphql' from origin 'http://localhost:5173' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
And now I'm getting this: createHttpLink.ts:166 POST https://venia.magento.com/graphql net::ERR_ABORTED 500 with error message: ServerParseError: Unexpected end of JSON input at JSON.parse (<anonymous>) at parseJsonBody
Does anybody else has this problem ? And what could be the solution? Thank you for your answers:)
main.js:
import { createApp, h, provide } from 'vue'
import './style.css'
import App from './App.vue'
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client/core'
import { DefaultApolloClient } from '#vue/apollo-composable'
// HTTP connection to the API
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: 'https://venia.magento.com/graphql',
fetchOptions: {
mode: 'no-cors',
},
headers:{
'Content-Type': 'application/graphql',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
}
})
// Cache implementation
const cache = new InMemoryCache()
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
const app = createApp({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App),
})
app.mount('#app');
and this is my App.vue:
<template>
</template>
<script setup>
import {useQuery} from '#vue/apollo-composable'
import { watchEffect } from '#vue/runtime-core';
import gql from 'graphql-tag';
const PRODUCTS_QUERY = gql`
query products {
products(filter: {sku: {eq: ""}}, pageSize: 5, currentPage: 3) {
items {
name
sku
price_range {
minimum_price {
regular_price {
value
currency
}
final_price {
value
currency
}
discount {
amount_off
percent_off
}
}
maximum_price {
regular_price {
value
currency
}
final_price {
value
currency
}
discount {
amount_off
percent_off
}
}
}
}
}
}
`;
const { result, loading, error } = useQuery(PRODUCTS_QUERY);
watchEffect(() => console.log(result))
</script>

Related

Failing to setup Websocket link

Been trying to get a Subscription working with Hasura and Vue Apollo with a websocket link with Vue Apollo with Vue3. Have it all seemingly setup.
The subscription works in Hasura so that’s right.
The query version worked with the HTTP link.
So the WS Link for some reason is just not working it. It seems like it might be authentication I’m not passing in correctly for some reason?
import './tailwind.css'
import App from './App.vue'
import { routes } from './routes.js'
import { createRouter, createWebHistory } from 'vue-router'
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client/core'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { createAuth0 } from '#auth0/auth0-vue';
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
import { HttpLink } from 'apollo-link-http'
const token = localStorage.getItem('Auth_token')
// HTTP connection to the API
const httpLink = new HttpLink({
// You should use an absolute URL here
uri: 'https://XXXXXXXXXXX.hasura.app/v1/graphql',
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "XXXXXXXXXXX",
"Authorization": `Bearer ${token}`,
}
})
// Create the subscription websocket link
const wsLink = new WebSocketLink({
uri: 'ws://XXXXXXXXXXX.hasura.app/v1/graphql',
options: {
reconnect: true,
timeout: 30000,
inactivityTimeout: 30000,
lazy: true,
},
connectionParams: {
headers: {
"content-type": "application/json",
// "x-hasura-admin-secret": "XXXXXXXXXXX",
"Authorization": `Bearer ${token}`,
}
}
})
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
},
wsLink,
httpLink
)
// Cache implementation
const cache = new InMemoryCache()
// Create the apollo client
const apolloClient = new ApolloClient({
link,
cache,
connectToDevTools: true,
})
const app = createApp({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: () => h(App),
})
const router = createRouter({
history: createWebHistory(),
routes,
})
// router.beforeEach(async (to, from) => {
// console.log("it's here", this.$auth0)
// // if (
// // // make sure the user is authenticated
// // ) {
// // // redirect the user to the login page
// // }
// })
app.use(router)
app.use(
createAuth0({
domain: "XXXXXXXXXXX",
client_id: "JgajoigAywNqoIyvQWNJjpq6TS3g5Ljn",
// redirect_uri: "http://localhost:3000/the-clouds"
redirect_uri: window.location.origin
})
);
app.mount('#app')
Main.JS file
subscription working
subscription in vue apollo front end
error 1
error 2
Figured it out! Was headers setup wrong. Wooh!
// Create the subscription websocket link
const wsLink = new WebSocketLink({
uri: 'ws://XXXXX-backend.hasura.app/v1/graphql',
options: {
reconnect: true,
timeout: 30000,
inactivityTimeout: 30000,
lazy: true,
connectionParams: {
headers: {
"content-type": "application/json",
"x-hasura-admin-secret": "XXXXX",
"Authorization": `Bearer ${token}`,
}
}
},
})

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

Data appears in console but does not render when I use a V-for Loop( Vuex and GraphQl)

I currently have my project set up in GraphQL and Vuex in my vue JS project, I was able to call my data from my Django backend but, I get errors when I try to render the data using a V-for Loop in Vue Js. The element just disappears.
Here is my GraphQL setup.
// Apollo client Setup
import Vue from 'vue'
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloLink } from 'apollo-link'
import VueApollo from 'vue-apollo'
// import { USER_ID, AUTH_TOKEN } from './constants/settings'
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
const token = localStorage.getItem(AUTH_TOKEN)
operation.setContext({
headers: {
authorization: token ? `JWT ${token}` : null
}
})
return forward(operation)
})
// HTTP connection to the API
const httpLink = createHttpLink({
// You should use an absolute URL here
uri: 'http://127.0.0.1:8000/graphql',
})
// Cache implementation
const cache = new InMemoryCache()
// Create the apollo client
export const apolloClient = new ApolloClient({
link: httpLink,
cache,
connectToDevTools: true,
})
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
})
Vue.use(VueApollo)
export default apolloProvider
Here is the setup in my Vuex store
import { apolloClient } from '../../apollo'
import {PORTFOLIO_LIST} from '../../graph/queries.js'
const state = {
portfolios:[],
portfolio:'',
};
const getters = {
allPortfolios:(state) => state.portfolios,
};
const mutations = {
SET_PORTFOLIOS(state, portfolios){
state.portfolios = portfolios
},
SET_PORTFOLIO(state, portfolio){
state.portfolio = portfolio
}
};
const actions = {
// The Portfolio list API
async portfolioList({commit}){
try{
const response = await apolloClient.query({query: PORTFOLIO_LIST})
const portfolios = JSON.stringify(response.data.portfolios)
commit('SET_PORTFOLIOS', portfolios)
console.log(portfolios)
}
catch(e){
console.log(e)
}
},
// The Portfolio Detail API
async portfolioDetail({commit}, ){
try{
const response = await apolloClient.query()
const portfolio = JSON.stringify(response.data)
console.log(portfolio)
commit ('SET_PORTFOLIO', portfolio)
}
catch(e){
console.log(e)
}
},
//The Portfolio Create API
// async portfolioCreate({commit}, ...portfolioDetail){
// },
// The Portfolio Delete API
// async portfolioDelete({commit}){
// },
};
export default {
state,
getters,
mutations,
actions,
}
And finally here is where I call my Vue Js for loop
<template>
<div class="px-6 lg:px-32 h-screen">
<section v-for ="portfolio in portfolios" :key="portfolio.id" class="mt-24 grid lg:grid-cols-3" >
<!-- The card section -->
<div>
{{portfolio.about}}
</div>
<!-- End of the card section -->
</section>
</div>
</template>
<script>
import { mapState} from 'vuex';
export default {
computed:{
...mapState(['portfolios']),
},
created(){
this.$store.dispatch('portfolioList');
}
}
</script>
I get a response in Vue JS dev tools, but it does not iterate in my Vue js code
When I open my console, The section where I iterated over, just disappears

How can i use GraphQl subscriptions in react-native chat application to get real-time updates from GraphQl queries

I am using GraphQl APIs in the react-native chat application. I want to get real-time updates when another user sends a message to me without refreshing the API. How can I do it using GraphQl API using GraphQl subscriptions or Websocket in react-native?
Should I use different URLs for subscription and normal API's?
Here is my config.js
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { HttpLink } from 'apollo-boost';
import { setContext } from 'apollo-link-context';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { AsyncStorage } from 'react-native';
// const httpLink = createHttpLink({
// uri: 'https://graphql.chat.dev.com/graphql',
// });
// const link = new HttpLink({
// uri: `https://graphql.chat.dev.com/graphql`,
// headers: {
// Authorization: AsyncStorage.getItem('#user_token');
// }
// });
const link = new WebSocketLink({
uri: `wss://graphql.chat.dev.com/graphql`,
options: {
reconnect: true,
connectionParams: {
headers: {
Authorization: AsyncStorage.getItem('#user_token');
}
}
}
})
const defaultOptions = {
query: {
fetchPolicy: "network-only",
errorPolicy: "all"
}
};
const client = new ApolloClient({
link: link,
cache: new InMemoryCache(),
defaultOptions
});
export default client;
I've not implemented Apollo with React Native but I did it with my React app. In my experience, you should use different URLs for subscription and normal APIs. Then, use import { split } from 'apollo-link' to split links, so you can send data to each link
depending on what kind of operation is being sent. You can read more about subscription in Apollo here.
This is my client.js file. Hopefully, it can help you.
import { ApolloClient } from 'apollo-client'
import { createUploadLink } from 'apollo-upload-client'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import { split } from 'apollo-link'
import { WebSocketLink } from 'apollo-link-ws'
import { getMainDefinition } from 'apollo-utilities'
const getToken = () => localStorage.getItem('AUTH_TOKEN')
const APOLLO_SERVER ="APOLLO_SERVER url"
const APOLLO_SOCKET ="APOLLO_SOCKET url"
// Create an http link:
const httpLink = createUploadLink({
uri: APOLLO_SERVER,
credentials: 'same-origin',
})
const authLink = setContext((_, { headers }) => {
const token = getToken()
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}
})
// Create a WebSocket link:
const wsLink = new WebSocketLink({
uri: APOLLO_SOCKET,
options: {
reconnect: true,
connectionParams: {
Authorization: getToken() ? `Bearer ${getToken()}` : '',
},
},
})
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
authLink.concat(httpLink)
)
const cache = new InMemoryCache()
const client = new ApolloClient({
cache,
link,
typeDefs,
resolvers,
})
This is my component where I integrate queries with subscriptions:
import React, { useEffect } from 'react'
import { useQuery } from '#apollo/react-hooks'
import gql from 'graphql-tag'
...
// query for querying message list
const GET_MESSAGE_LIST = gql`...`
// subscription for listening new message
const ON_MESSAGE_CREATED = gql`...`
const ChatView = props => {
const { data, loading, subscribeToMore } = useQuery(GET_MESSAGE_LIST, {
{
notifyOnNetworkStatusChange: true,
variables: {
query: {
limit: 10,
userId: props.userId,
},
},
}
})
useEffect(() => {
subscribeToMore({
document: ON_MESSAGE_CREATED,
variables: { filter: { userId: props.userId } },
shouldResubscribe: true,
updateQuery: (prev, { subscriptionData }) => {
let newMessage = subscriptionData.data.onZaloMessageCreated
return Object.assign({}, prev, {
messageList: {
...prev.messageList,
items:
prev.messageList.items.filter(
item => item.id === newMessage.id
).length === 0
? [newMessage, ...prev.messageList.items]
: prev.messageList.items,
},
})
},
})
}, [subscribeToMore])
return ...
}

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