check for internet connectivity using WebSocketLink from apollo-link-ws - websocket

I'm trying to check for internet connectivity using apollo websockets, the purpose of this is to show a "you're disconnected" message when there is no connection to prevent the user from typing and assuming the changes are saved (the changes are supposedly saved on type), here's part of the setup of apollo-link-ws
const wsLink = new WebSocketLink({
uri: `ws://${hostname}${port ? `:${port}` : ''}/subscriptions`,
options: {
reconnect: true,
connectionParams: () => ({
authorization: localStorage.getItem('accelerator-token')
})
}
});
and
const hasSubscriptionOperation = ({ query: { definitions } }) =>
definitions.some(
({ kind, operation }) =>
kind === 'OperationDefinition' && operation === 'subscription'
);
and here's the client config:
const client = new ApolloClient({
link: ApolloLink.split(
hasSubscriptionOperation,
wsLink,
ApolloLink.from([
cleanTypenameLink,
authMiddleware,
errorLink,
stateLink,
createUploadLink()
])
),
cache
});

After some searching i found that i can use SubscriptionClient from subscriptions-transport-ws
export const myClient = new SubscriptionClient(`ws://${hostname}${port ?
`:${port}` : ''}/subscriptions`, {
reconnect: true,
connectionParams: () => ({
authorization: localStorage.getItem('accelerator-token')
})
});
myClient.onConnected(()=>{console.log("connected f client f onConnected")})
myClient.onReconnected(()=>{console.log("connected f client f
reconnected")})
myClient.onReconnecting(()=>{console.log("connected f client f
reconnecting")})
myClient.onDisconnected(()=>{console.log("connected f client f
onDisconnected")})
myClient.onError(()=>{console.log("connected f client f onError")})
export const wsLink = new WebSocketLink(myClient);
These methods can be used to detect the network status

If you are working with React I found this nice community package react-apollo-network-status

Related

Apollo Client - Simultaneous subscriptions from same component

I'm trying to make 2 simultaneous subscriptions with Apollo Client but the connection get closed and reopened every 2 seconds:
This is my code concerning subscriptions:
//apollo.js
const httpLink = createHttpLink({
includeUnusedVariables: true,
uri:
process.env.API_GRAPHQL ||
// Change to your graphql endpoint.
headers: {
Authorization:
"Bearer TOKEN",
},
});
const wsLink = new GraphQLWsLink(
createClient({
url: process.env.WS_GRAPHQL,
connectionParams: {
Authorization:
"Bearer TOKEN",
},
options: {
reconnect: true,
},
})
);
const link = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
// subscriber executer
const {
result: locationUpdates,
// loading: loadingLocation,
// error: devicesError,
// refetch: refetchDevices,
onResult: onResultLocations,
} = useSubscription(locationsLivesTrue, () => ({
}));
const { result: me, onResult: onResultMe } = useSubscription(
meUpdates,
() => ({})
);
If I execute only one subscription it works fine.
I also tried to subscribe directly from the client when I provide the app, but got the same result.
#juanmac My original post was deleted so I will answer here. Since you asked me a question there, I think it is fine I will answer inside your newest post ;)
A loop was used. Inside the loop, a subscribeToMore was used.
Inside that function, updateQuery was used.
There were some problems but I do not know if they were resolved. I will remind you, that it was React Native, and there are some stability issues with subscriptions etc.
I hope that helps.

How to pass different Apollo client to useMutation graphql

I am new to graphql and react. Currently one apollo client is used by default for all useQuery and useMutation which was intialized via ApolloProvider.
Now I have to pass different apollo clients (having different uri) to different queries and mutations. I am able to pass different clients in useQuery() but not able to do the same in useMutation().
// Client initialized via ApolloProvider
const client = new ApolloClient({
cache: new InMemoryCache(),
link: httpLink1,
});
// Custom client with different http link
const customClient = new ApolloClient({
cache: new InMemoryCache(),
link: httpLink2,
});
// This is working where I can see it is using httpLink2
const { loading, error, data } = useQuery(GET_ITEMS, {
client: customClient
});
const [myMutation] = useMutation(LOAD_ITEMS)
const loadItem (): void => {
// not working. By default, the apollo client instance that's passed down via context is use.
const variables = { item: 1, client: customClient }
myMutation({ variables })
// rest of the code
}
As per below useMutation() documentation I can see that we can pass different clients.
https://www.apollographql.com/docs/react/data/mutations/#client. But somehow it is not working for me.
Could someone please help me here.
Thanks in advance.
You can use the same apollo client but use the httpLink conditionally.
https://www.apollographql.com/docs/react/api/link/introduction#providing-to-apollo-client
Creating the link:
import { ApolloLink, HttpLink } from '#apollo/client';
const directionalLink = ApolloLink.split(
operation => operation.getContext().clientName === "second",
new HttpLink({ uri: "http://localhost:4000/v1/graphql" }),
new HttpLink({ uri: "http://localhost:4000/v2/graphql" })
),
Initializing apollo client:
const client = new ApolloClient({
cache: new InMemoryCache(),
link: directionalLink
});
In the component:
const {data, error, loading} = useQuery(GET_STUFF, {
context: { version: "second" }
});

RTK type Error when using injectedEndpoints with openApi

I define config file for openApi to create automatically endpoints with types:
const config: ConfigFile = {
schemaFile: 'https://example.com/static/docs/swagger.json',
apiFile: './api/index.ts',
apiImport: 'api',
outputFile: './api/sampleApi.ts',
exportName: 'sampleApi',
hooks: true,
};
export default config;
I used :
"#rtk-query/codegen-openapi": "^1.0.0-alpha.1"
"#reduxjs/toolkit": "^1.7.2",
Then I define an index.tsx that has
export const api = createApi({
baseQuery: axiosBaseQuery({ baseUrl: '' }),
endpoints: () => ({}),
});
and So I generate successfully my sampleApi.tsx file with all of endpoints and types.
like here:
const injectedRtkApi = api.injectEndpoints({
endpoints: (build) => ({
postUsersCollections: build.mutation<
PostUsersCollectionsApiResponse,
PostUsersCollectionsApiArg
>({
query: (queryArg) => ({
url: `/users/collections`,
method: 'POST',
body: queryArg.postCollectionBody,
}),
}),
getUsersCollections: build.query<
GetUsersCollectionsApiResponse,
GetUsersCollectionsApiArg
>({
query: (queryArg) => ({
url: `/users/collections`,
params: { name: queryArg.name },
}),
}),
overrideExisting: false,
});
export const {
usePostUsersCollectionsMutation,
useGetUsersCollectionsQuery
} = injectedRtkApi;
when in a component I use hook function useGetUsersCollectionsQuery as bellow I got an error that TypeError: Cannot read properties of undefined (reading 'subscriptions'). There is no lint typescript error related to typescript in my project.
const { data: collectionData = [] } = useGetUsersCollectionsQuery({});
It's Interesting that this hook called and I see API call in network tab but immediately I got this error. I remove this line and error is disappeared.
And Also for mutation hook I send data within it but I got 400 error. as Below:
const [postCollection, { data: newCollect }] =
usePostUsersCollectionsMutation();
...
const handleCreateItem = async () => {
const response: any = await postCollection({
postCollectionBody: { name: 'sample' },
}); }
Please help me! I really thanks you for taking time.
Finally I resolved it!
I should define reducerPath as this:
export const api = createApi({
reducerPath: 'api', <=== add this and define `api` in reducers
baseQuery: axiosBaseQuery({ baseUrl: '' }),
endpoints: () => ({}),
});

Is there a way to name graphql requests in the devtool network tab?

I'm using apollo as my client and I run plenty of queries and mutations on my app. I was wondering if there is a way to have each of my query/mutation displayed by its name (eg. getProduct) instead of all showing as "graph" in my network tab? I'm on Brave (Chromium).
It would make debugging easier if I didn't have to click on each one and check the headers or the response to identify which query or mutation this request corresponds to.
Here's how it currently shows in my devtools:
network tab screenshot
Thanks a lot!
Maybe there is a better way but here the minimal code I could do to make it.
import {
ApolloClient,
ApolloLink,
HttpLink,
InMemoryCache,
} from '#apollo/client';
const httpLink = new HttpLink({ uri: MY_BASE_URL });
const namedLink = new ApolloLink((operation, forward) => {
operation.setContext(() => ({
uri: `${MY_BASE_URL}?${operation.operationName}`,
})
);
return forward ? forward(operation) : null;
});
export const client = new ApolloClient({
link: ApolloLink.from([namedLink, httpLink]),
cache: new InMemoryCache(),
});
You'll have to name your query :
import { gql } from "#apollo/client";
const QUERY = gql`
query QueryName {
...
}
`;
Hope it'll help.
uri prop of HttpLink can accept function which have operation as an arg
so it can be done like this as well:
const httpLink = new HttpLink({ uri: (operation) => `${MY_BASE_URL}?${operation.operationName}` });

How to cache using apollo-server

The apollo basic example at https://www.apollographql.com/docs/apollo-server/features/data-sources.html#Implementing-your-own-cache-backend they state that doing a redis cache is as simple as:
const { RedisCache } = require('apollo-server-cache-redis');
const server = new ApolloServer({
typeDefs,
resolvers,
cache: new RedisCache({
host: 'redis-server',
// Options are passed through to the Redis client
}),
dataSources: () => ({
moviesAPI: new MoviesAPI(),
}),
});
When I look at the examples of non-redis, it states that it's a simple { get, set } for cache. This means I should theoretically be able to do.
cache : {
get : function() {
console.log("GET!");
},
set : function() {
console.log("SET!");
}
}
No matter what I try, my cache functions are never called when I'm utilizing the graphQL explorer that apollo-server provides natively.
I have tried with cacheControl : true and with cacheControl set like it is in https://medium.com/brikl-engineering/serverless-graphql-cached-in-redis-with-apollo-server-2-0-f491695cac7f . Nothing.
Is there an example of how to implement basic caching in Apollo that does not utilize the paid Apollo Engine system?
You can look at the implementation of this package which caches the full response to implement your own cache.
import { RedisCache } from "apollo-server-redis";
import responseCachePlugin from "apollo-server-plugin-response-cache";
const server = new ApolloServer({
...
plugins: [responseCachePlugin()],
cache: new RedisCache({
connectTimeout: 5000,
reconnectOnError: function(err) {
Logger.error("Reconnect on error", err);
const targetError = "READONLY";
if (err.message.slice(0, targetError.length) === targetError) {
// Only reconnect when the error starts with "READONLY"
return true;
}
},
retryStrategy: function(times) {
Logger.error("Redis Retry", times);
if (times >= 3) {
return undefined;
}
return Math.min(times * 50, 2000);
},
socket_keepalive: false,
host: "localhost",
port: 6379,
password: "test"
}),
});
You should be able to use the NPM package 'apollo-server-caching' by implementing your own interface. See Implementing Your Own Cache which provides an example.

Resources