I am getting this Websocket failed to Connect error for both client and server side now (as shown in the image below). I am not using any other Websocket configuration other than the one specified in the apollo client. This has been baffling me for about 2 days. Any help would be appreciated. Let me know if you need to see any further code.
I have a Vue app client that connects to graphql apollo server. The code for apolloclient configuration is given below.
// Apollo packages
import { ApolloClient } from "apollo-boost-upload";
import { WebSocketLink } from "apollo-link-ws";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
import { split } from "apollo-link";
import { getMainDefinition } from "apollo-utilities";
import VueApollo from "vue-apollo";
Vue.use(VueApollo);
wsLink = new WebSocketLink({
uri: "ws://localhost:4000/graphql", // use wss for a secure endpoint
options: {
reconnect: true,
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
// Cache implementation
export const defaultClient = new ApolloClient({
// uri: "http://localhost:4000/graphql",
link,
cache: new InMemoryCache(),
fetchOptions: {
credentials: "include",
},
request: (operation) => {
// if no token in local storage, add it
if (!localStorage.anaceptToken) {
localStorage.setItem("anaceptToken", "");
}
// operation adds the token to authorizatrion header, which is sent o backend
operation.setContext({
headers: {
authorization: "Bearer " + localStorage.getItem("anaceptToken"),
},
});
},
onError: ({ graphQLErrors, networkError }) => {
if (networkError) {
console.log("[networkError]", networkError);
}
if (graphQLErrors) {
for (const error of graphQLErrors) {
console.dir(error);
console.log(error);
if (
error.name === "AuthenticationError" ||
error.message === "jwt expired"
) {
// set auth error in state
store.commit("setError", error);
// signout user to clear error
store.dispatch("signUserOut");
}
}
}
},
});
vue config file
const { defineConfig } = require("#vue/cli-service");
const NodePolyfillPlugin = require("node-polyfill-webpack-plugin");
module.exports = defineConfig({
pluginOptions: {
apollo: {
enableMocks: true,
enableEngine: true,
},
},
transpileDependencies: ["vuetify"],
chainWebpack: (config) => {
config.performance.maxEntrypointSize(400000).maxAssetSize(400000);
new NodePolyfillPlugin();
},
});
interesting try localhost 4004, it should work
Related
import {
createClient,
defaultExchanges,dedupExchange, cacheExchange, fetchExchange,
subscriptionExchange,
gql
} from "#urql/core";
import { createClient as createWSClient } from "graphql-ws";
import { pipe, subscribe } from "wonka";
import { getToken, setToken } from "./helper";
const wsClient = createWSClient({
url: 'wss://**********/subscriptions',
reconnect: true,
});
const client = createClient({
url: "https://***********/",
fetchOptions: () => {
const token = getToken()
return token ? { headers: { authorization: `Bearer "${token}"` } } : {}
},
// the default:
exchanges: [
...defaultExchanges,
subscriptionExchange({
forwardSubscription(operation) {
return {
subscribe: (sink) => {
const dispose = wsClient.subscribe(operation, sink);
return {
unsubscribe: dispose,
};
},
};
},
}),
]
});
SUB_TO_MESSAGES = async () => {
console.log('sub')
const token = getToken();
console.log(String(token))
const { unsubscribe } = pipe(
await client.subscription(messageAdded,{ jwt: token }),
subscribe((result) => {
console.log(result)
})
)
};
I dont get the same issue with try and catch using GraphQL-WS but I still dont get any data from the server. The assignment is a vanillaJS project using GraphQL.I didndt post the url, jwt token,or the GET, POST, REgG as they work as intended. The rendering is done with a proxy. The error message is:
Connection Closed: 4500 Cannot read properties of undefined (reading 'Authorization')
Even playground doesnt work. Something wrong with the endpoint. It worked 2 weeks ago but admin says it still work yet I can find the problem. It used to work for me.
Here is the try and catch version:
import { createClient} from "graphql-ws";
import pStore from "./handler.js";
import { getToken } from "./helper";
const client = createClient({
url: "wss://******/subscriptions",
reconnect: true,
connectionParams:{
headers: {
"Authorization":`Bearer ${getToken()}`
}
},
})
async SUB_MESSAGE() {
try {
console.log('called Gql server')
const onNext = (res) => {
let obj = res.data.messageAdded
console.log(obj)
pStore[obj.id] = obj
pStore.render(obj)
};
let unsubscribe = () => {
/* complete the subscription */
};
new Promise((resolve, reject) => {
client.subscribe({
query: `subscription{messageAdded(jwt:"${getToken()}"){id text fromAgent createdAt updatedAt}}`,
},
{
next: (data)=> onNext(data),
error: reject,
complete: () => resolve(true),
})
})
}catch(error){
console.error('There has been a problem with your ws operation:', error);
}
}
Either way I think its a ad character, scope issue but I dont know where.
I am trying to fetch protected resource from my graphql server using nextJs and apollo client. I stored the authorization token in the client browser (localstorage) and try to read the token from apolloClient.Js file; but it throws a ReferenceError (ReferenceError: localStorage is not defined). This makes me to understand quickly that the server side was trying to reference localStorage from the backend; but fails because it is only available in the client. My question is, what is the best way to solve this issue? I am just using apollo client for the first time in my project. I have spent more than 10 hours trying to figure out the solution to this problem. I have tried so many things on web; not lucky to get the solution. Here is the code am using in apolloClient file:
import { useMemo } from 'react'
import { ApolloClient, HttpLink, InMemoryCache } from '#apollo/client'
import { concatPagination } from '#apollo/client/utilities'
import { GQL_URL } from '../utils/api'
let apolloClient
const authToken = localStorage.getItem('authToken') || '';
function createApolloClient() {
return new ApolloClient({
ssrMode: typeof window === 'undefined',
link: new HttpLink({
uri: GQL_URL, // Server URL (must be absolute)
credentials: 'include', // Additional fetch() options like `credentials` or `headers`
headers: {
Authorization: `JWT ${authToken}`
}
}),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
allPosts: concatPagination(),
},
},
},
}),
})
}
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
// If your page has Next.js data fetching methods that use Apollo Client, the initial state
// gets hydrated here
if (initialState) {
_apolloClient.cache.restore(initialState)
}
// For SSG and SSR always create a new Apollo Client
if (typeof window === 'undefined') return _apolloClient
// Create the Apollo Client once in the client
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
export function useApollo(initialState) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}
I was able to solve the problem by accessing the local storage only when the window object is not 'undefined'; since it will be 'undefined' in the server side. This will work well because we don't want the server to access local storage.
import { useMemo } from 'react'
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client';
import { setContext } from '#apollo/client/link/context';
import { GQL_URL } from '../utils/api'
let apolloClient
function createApolloClient() {
// Declare variable to store authToken
let token;
const httpLink = createHttpLink({
uri: GQL_URL,
credentials: 'include',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
if (typeof window !== 'undefined') {
token = localStorage.getItem('authToken');
}
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
Authorization: token ? `JWT ${token}` : "",
}
}
});
const client = new ApolloClient({
ssrMode: typeof window === 'undefined',
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
return client;
}
I can see this issue has been solved. But only partially. Right now this is fine for making authorized client-side queries but if someone is trying to make an authorized query on the server-side, then this would be an issue as it doesn't have access to local storage.
So modifying this :
//AUTH_TOKEN is the name you've set for your cookie
let apolloClient;
const httpLink = createHttpLink({
uri: //Your URL,
});
const getAuthLink = (ctx) => {
return setContext((_, { headers }) => {
return {
headers: {
...headers,
authorization: isSSR()
? ctx?.req?.cookies[AUTH_TOKEN] // server-side auth token
: getPersistedAuthToken(), /* This is your auth token from
localstorage */
},
};
});
};
function createApolloClient(ctx) {
return new ApolloClient({
ssrMode: typeof window === undefined,
link: from([getAuthLink(ctx), httpLink]),
cache: new InMemoryCache(),
});
}
export function initializeApollo({ initialState = null, ctx = null }) {
const _apolloClient = apolloClient ?? createApolloClient(ctx);
if (initialState) {
const existingCache = _apolloClient.extract();
_apolloClient.cache.restore({ ...existingCache, ...initialState });
}
if (isSSR()) return _apolloClient;
if (!apolloClient) apolloClient = _apolloClient;
return _apolloClient;
}
The getServerSide function would look like this:
export async function getServerSideProps(ctx) {
const { req } = ctx;
if (req?.cookies[AUTH_TOKEN]) {
const apolloClient = initializeApollo({ initialState: null, ctx });
try {
const { data } = await apolloClient.query({
query: GET_USER_DETAILS,
});
// Handle what you want to do with this data / Just cache it
} catch (error) {
const gqlError = error.graphQLErrors[0];
if (gqlError) {
//Handle your error cases
}
}
}
return {
props: {},
};
}
This way the apollo client can be used to make authorized calls on the server-side as well.
I'm working on a Vue app that uses Axios and Wordpress JWT Auth for authentication. Recently I started getting this 401 when trying to post (100% of the time) ...but... only on Mac OS X and iOS.
Can you point me in the right direction as to how to troubleshoot this? The error object in Chrome console network is this:
{"code":"rest_forbidden","message":"Sorry, you are not allowed to do that.","data":{"status":401}}
My post looks like this:
https://wordpressroot.ourdomain.com/wp-json/jwt-auth/v1/folder/transfer {location_id: "rec4ttKVWJCDr8v", items: Array(1)}
axios localClient:
import axios from "axios";
import environment from "#/environments/environment";
import state from "../store";
import router from "../router";
const userData = JSON.parse(localStorage.getItem("userData"));
let instance = {};
if (userData) {
instance = axios.create({
baseURL: environment.CUSTOM_BASE_URL,
headers: { Authorization: `Bearer ${userData.token}` }
});
} else {
instance = axios.create({
baseURL: environment.CUSTOM_BASE_URL
});
}
instance.interceptors.request.use(
config => {
state.commit("setNetworkStatus", true);
return config;
},
error => {
return Promise.reject(error);
}
);
instance.interceptors.response.use(
response => {
state.commit("setNetworkStatus", false);
return response;
},
error => {
if ([401, 403].includes(error.response.status)) {
console.log(error);
state.commit("delUserData");
router.push("/login");
}
return Promise.reject(error);
}
);
export default {
get(path) {
return instance.get(instance.defaults.baseURL + path);
},
post(path, params) {
console.log(instance.defaults.baseURL + path, params);
return instance.post(instance.defaults.baseURL + path, params);
},
put(path, params) {
return instance.put(instance.defaults.baseURL + path, params);
},
delete(path, params) {
return instance.delete(instance.defaults.baseURL + path, params);
}
};
I want to add onError to my index.js Apollo file. So that video helped me how a very basic example looks like. But as I have some more links in my project, it's a bit different to what is shown there.
Index.js:
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
import { WebSocketLink } from 'apollo-link-ws'
import { split } from 'apollo-link'
import { onError } from "apollo-link-error";
const httpLink = createHttpLink({
uri: 'http://localhost:4000',
})
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem(AUTH_TOKEN)
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
}
})
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000`,
options: {
reconnect: true,
connectionParams: {
authToken: localStorage.getItem(AUTH_TOKEN),
},
},
})
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query)
return kind === 'OperationDefinition' && operation === 'subscription'
},
wsLink,
authLink.concat(httpLink),
)
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
})
Now I want to add the errorLink to my project to track error with this code:
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message, location, path }) =>
console.log(`[GraphQL error]: Message: ${message}, Location: ${location}, Path: ${path}`),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
But I'm not sure how to add that new link to the link const. Is it done with a concat or something else?
I already had a look on the composing links section. But that's also too different from my example.
split is returning a new ApolloLink.
In this case, doing link: ApolloLink.from([errorLink, link]) should work. It will create a new ApolloLink from an array of ApolloLink.
As I also wanted to not let the user run into an error message. This is one method that prevents the frontend from showing the error:
const defaultOptions = {
query: {
errorPolicy: 'all',
},
mutate: {
errorPolicy: 'all'
}
}
const client = new ApolloClient({
link: ApolloLink.from([errorLink, link]),
cache: new InMemoryCache(),
defaultOptions,
})
I have a simple code:
import { split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws'
import { HttpLink } from 'apollo-link-http'
import ApolloClient from 'apollo-client'
import { onError } from 'apollo-link-error'
const wsLink = new WebSocketLink({
uri: hasura.wsUrl,
options: {
reconnect: true,
timeout: 30000,
connectionParams: {
headers: {
'Authorization': `Bearer ${this.token}`
}
}
}
})
const httpLink = new HttpLink({
uri: hasura.httpUrl,
headers: {
'Authorization': `Bearer ${this.token}`
}
})
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httpLink
)
const errorLink = onError(({graphQLErrors, networkError}) => {
// this callback is never called
console.log('graphQLErrors', graphQLErrors)
console.log('networkError', networkError)
})
this.client = new ApolloClient({
link: errorLink.concat(link),
cache: new InMemoryCache()
})
How I can a handling errors for the "split" links? For this example catching errors doesn't works. If I use links without "split" function errors catching works.
let link = ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) console.error(`[Network error]: ${networkError}`, networkError.stack);
}),
ApolloLink.split(
operation => operation.getContext().important === true,
httpLink, // don't batch important
batchHttpLink
),
]);