I am try to use #apollo/client in my nextjs application (only frontend). My backend is done using nestjs-graphql. I am facing one problem using #apollo/client.
Here is my code-
client.ts
//Initialize apolloClient
let apolloClient: ApolloClient<NormalizedCacheObject>;
//Creating link
function createIsomorphLink() {
return new HttpLink({
uri: "http://localhost:5000/piechat",
credentials: 'same-origin'
})
}
//Create apollo client
function createApolloClient() {
return new ApolloClient({
ssrMode: true,
link: createIsomorphLink(),
cache: new InMemoryCache()
})
}
//Initialize Apollo
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
if (initialState) {
const existingCache = _apolloClient.extract()
const data = merge(initialState, existingCache)
_apolloClient.cache.restore(data)
}
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
//Use Apollo
export function useApollo(initialState = null) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}
Then in nextjs getServerSideProps-
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const apolloClient = initializeApollo()
await apolloClient.query({
query: GET_CONVERSATION_LIST,
context: { headers: { authorization: `Bearer ${ctx.req.cookies["session"]}` } },
errorPolicy: "all"
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
}
}
}
Then in one conversation.tsx(I use this data)-
const { data } = useQuery<GetConversationData>(GET_CONVERSATION_LIST);
On the other component (createConver.tsx) I use fetchMore function-
const { fetchMore } = useQuery(GET_CONVERSATION_LIST)
useEffect(() => {
if (data) { //If new data creation is success
fetchMore({
context: { headers: { authorization: `Bearer ${getCookie("session")}` } }, updateQuery(_, { fetchMoreResult }) {
return fetchMoreResult
}
})
}
}, [data])
Everything is well. But when I refresh my page, I see that new data not comes. please see this gif-
But I am not understanding what is the problem. I am not getting any docs or any things else. Please help me anyone.
Related
I write a hook named useGoogleCalendarEvents, Try to get users google calendar event,below is my code.
import { Schedule, User } from '../../types/commons.type';
import { useLocalStorage } from 'usehooks-ts';
import useSWR from 'swr';
import { FetchCalendarList } from '../../types/googleCalendarAPIResponse.type';
import { currentUTCMonthString } from '../../utils/DateUtils';
const baseURL = 'https://www.googleapis.com/calendar/v3';
const fetcher = ([path, method, body = {}, accessToken = '']: [string, string, object, string]) => {
const headers = {
'Content-Type': ['POST', 'PUT', 'PATCH'].includes(method) ? 'application/json' : '',
Authorization: `Bearer ${accessToken}`,
};
const fetchOptions = {
method,
headers,
body: method === 'GET' ? undefined : JSON.stringify(body),
};
const apiURL = `${baseURL}${path}`;
return fetch(apiURL, fetchOptions).then((r) => r.json());
};
export const useGoogleCalendarEvents = () => {
const [user, setUser] = useLocalStorage<User | undefined>('user', undefined);
const events: Array<string> = [];
if (user?.googleAccessToken) {
// get user calendar list
const { data: GoogleCalendarListResult } = useSWR<FetchCalendarList>(
['/users/me/calendarList', 'GET', {}, user?.googleAccessToken],
fetcher,
);
const { startString, endString } = currentUTCMonthString();
const parameters = `timeMin=${startString}&timeMax=${endString}`;
// map loop to get event by calendar id
GoogleCalendarListResult?.items?.map((calendar) => {
const { data: GoogleCalendarEventResult } = useSWR(
[`/calendars/${calendar.id}/events?${parameters}`],
fetcher,
);
});
}
return { events };
};
the code not yet finish but i got the error
"React has detected a change in the order of Hooks called by MySettingPage2."
I modify the code
GoogleCalendarListResult?.items?.map((calendar) => {
const { data: GoogleCalendarEventResult } = useSWR(
[`/calendars/${calendar.id}/events?${parameters}`],
fetcher,
); });
to
['calendarid1', 'calendarid2'].map((calendar) => {
const { data: GoogleCalendarEventResult } = useSWR(
[`/calendars/${calendar.id}/events?${parameters}`],
fetcher,
);
});
It is work, but not flexble. I want to get calendar id list first, then get envets by calendar id
Is anybody known why cause this error? Thank for any reply
I am try to use #apollo/client in my nextjs application (only frontend). My backend is done using nestjs-graphql. I am facing one problem using #apollo/client.
Here is my code-
client.ts
//Initialize apolloClient
let apolloClient: ApolloClient<NormalizedCacheObject>;
//Creating link
function createIsomorphLink() {
return new HttpLink({
uri: "http://localhost:5000/piechat",
credentials: 'same-origin'
})
}
//Create apollo client
function createApolloClient() {
return new ApolloClient({
ssrMode: true,
link: createIsomorphLink(),
cache: new InMemoryCache()
})
}
//Initialize Apollo
export function initializeApollo(initialState = null) {
const _apolloClient = apolloClient ?? createApolloClient()
if (initialState) {
const existingCache = _apolloClient.extract()
const data = merge(initialState, existingCache)
_apolloClient.cache.restore(data)
}
if (!apolloClient) apolloClient = _apolloClient
return _apolloClient
}
//Use Apollo
export function useApollo(initialState = null) {
const store = useMemo(() => initializeApollo(initialState), [initialState])
return store
}
Then in nextjs getServerSideProps-
export const getServerSideProps: GetServerSideProps = async (ctx) => {
const apolloClient = initializeApollo()
await apolloClient.query({
query: GET_CONVERSATION_LIST,
context: { headers: { authorization: `Bearer ${ctx.req.cookies["session"]}` } },
errorPolicy: "all"
})
return {
props: {
initialApolloState: apolloClient.cache.extract(),
}
}
}
Then in one conversation.tsx(I use this data)-
const { data } = useQuery<GetConversationData>(GET_CONVERSATION_LIST);
On the other component (createConver.tsx) I use fetchMore function-
const { fetchMore } = useQuery(GET_CONVERSATION_LIST)
useEffect(() => {
if (data) { //If new data creation is success
fetchMore({
context: { headers: { authorization: `Bearer ${getCookie("session")}` } }, updateQuery(_, { fetchMoreResult }) {
return fetchMoreResult
}
})
}
}, [data])
Everything is well. But when I refresh my page, I see that new data not comes. please see this gif- enter image description here
But I am not understanding what is the problem. I am not getting any docs or any things else. Please help me anyone.
I am using Graphql useSubscription hook to hit a websocket api but there seems to be an issue. I am only receiving data when i enter the component for the first time or when i go back to some other component and come back again, when trying to refresh majority of the times I do not get the data.
Below is my setup for the following.
/* eslint-disable flowtype/no-types-missing-file-annotation */
import Cookies from 'js-cookie'
import { split, HttpLink, InMemoryCache, ApolloClient } from '#apollo/client'
import { setContext } from '#apollo/client/link/context'
import { WebSocketLink } from '#apollo/client/link/ws'
import { getMainDefinition } from '#apollo/client/utilities'
import { onError } from 'apollo-link-error'
import { ApolloLink } from 'apollo-link'
const env = process.env.NODE_ENV
const domain = env === 'development' ? 'localhost' : '.xyz'
const url = env === 'development' ? 'https://staging-xxx.xxxx.xx' : process.env.REACT_APP_API_URL;
const wsURL = env === 'development' ? 'wss://staging-xxx.xxxxx.xx/subscriptions' : process.env.REACT_APP_WSS_URL;
const httpLink = new HttpLink({
uri: url,
credentials: 'include'
})
const authLink = setContext((_: any, { headers }: any) => {
const app_token = Cookies.get('xxxxx', { domain: domain })
let token = app_token || 'insta-checkout'
return {
headers: {
...headers,
MUDEY_AUTH_TOKEN: token
}
}
})
const wsLink = new WebSocketLink({
uri: wsURL,
options: {
reconnect: true,
connectionParams: async () => {
const app_token = await Cookies.get('xxxxx', { domain: domain })
return {
credentials: 'include',
MUDEY_AUTH_TOKEN: app_token,
Authorization: 'Basic xxxxxxxxxxxxxxxxxxx'
}
}
}
})
const link = split(
({ query }: any) => {
const definition = getMainDefinition(query)
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription'
},
wsLink,
authLink.concat(httpLink)
)
const client = new ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([link])
})
export default client
Now when i go my component for the first time, i am calling the subscriptions api by
import React from "react";
import { useMutation, useSubscription } from "#apollo/react-hooks";
export const NewComponent = () => {
const {
loading: loadingPackages,
data,
error,
} = useSubscription(SUBSCRIBE_CAR_PACKAGES, {
onSubscriptionData: useCallback((res: any) => {
const {
subscribeCarJourneyPackages: { message: stopWS, data: packagesResult },
} = res.subscriptionData.data;
if (packagesResult !== null) {
console.log("packarray", packagesResult);
setIsSubsLoading(true);
}
if (stopWS === "SUBSCRIPTION_COMPLETE") {
dispatch({ type: SET_ALL_PACKAGES, payload: packArray });
setIsSubsLoading(false);
} else {
// setIsSubsLoading(true)
}
}, []),
onError: useCallback((err: any) => {
apiErrorHandler(err);
}, []),
variables: { id: journeyID },
});
return null;
};
So the response i see is
But once i start refreshing the page , i only see
So what the issue in my frontend, for not getting the response 100% of the time ? should we need to close the connection everytime we receive response ?
Also i see the subscription api hitting even when i am in my homepage, where ideally it should hit in the results page where i want it, do this happens the moment we define connection and is it normal?
I know it's late but it maybe help someone else !
try giving fetchPolicy: 'cache-and-network' as option below your variables field.
like this :
variables: {... your variables },
fetchPolicy: 'cache-and-network'
I'm building an App with Next.js, and I need to connect to specific API routes (set up with API Platform) and populate pages with the route's responses.
The API is working fine, but no matter how I try to implement my Axios call inside the getServerSideProps, I always get the same error, ECONNREFUSED, from my Node stack.
I tried to get the data from useEffect() and it's working fine, but I would like to know if there's a way to call it directly in getServerSideProps.
I'm using a Node container for Docker, and the routes are authenticated through a JWT Token (stored in the session and the client cookies for the server-side connection)
Here are is my code:
pages/accounts.js:
export async function getServerSideProps(context) {
const cookies = new Cookies(context.req.headers.cookie)
const adminToken = cookies.get('jwtToken')
const res = await getAllAccounts(adminToken)
return {
props: {
testdata: ''
},
}
}
lib/accounts.js:
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`).then((response) => {
}).catch((error) => {
console.dir(error)
})
}
HTTP wrapper:
import axios from 'axios';
import jwt_decode from "jwt-decode";
import mockAdapter from 'axios-mock-adapter';
const service = ({ jwtToken = null, store = null, mockURL = null, mockResponse = null, multipart = false } = {}) => {
const options = {};
options.baseURL = process.env.NEXT_PUBLIC_API_URL + '/api';
if(multipart === true) {
options.headers = {
'Content-Type': 'multipart/form-data'
}
} else {
options.headers = {
'Content-Type': 'application/ld+json',
accept: 'application/ld+json'
}
}
const instance = axios.create(options);
instance.interceptors.response.use(response => {
return response;
}, error => {
return Promise.reject(error);
})
if (mockURL !== null && mockResponse !== null) {
let mock = new mockAdapter(instance);
mock.onAny(mockURL).reply(200, mockResponse)
}
return instance;
};
export default service;
Through the error dump in the node stack, I managed to see that the request headers are correct, and the JWT correctly passed through.
Do not use Axios. Just use fetch().
Next.js polyfills fetch() by default on both the client and server, so you can just use it:
In addition to fetch() on the client-side, Next.js polyfills fetch() in the Node.js environment. You can use fetch() in your server code (such as getStaticProps/getServerSideProps) without using polyfills such as isomorphic-unfetch or node-fetch.
Source.
getServerSideProps works well with axios if we return response.data
export const getServerSideProps: GetStaticProps = async ({ params }) => {
const { brandName } = params as IParams;
const brandData = await $host.get(`api/brand/${brandName}`).then(response => response.data);
return {
props: {
brand: brandData,
},
};
};
Your problem is that your async method does not return a promise.
import service from '../service'
export const getAllAccounts = async (adminToken) => {
const res = service({ jwtToken : adminToken }).get(`/accounts`);
return res;
}
In my NextJS begining I followed this tutorial , and I changed fetch to axios in this way:
export const getStaticPaths = async () => {
const res = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await res.json();
const paths = data.map((ninja) => {
return {
params: { id: ninja.id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.id;
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
const data = await res.json();
return {
props: { ninja: data },
};
};
I applied the change using useEffect()
useEffect(() => {
// const data = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
// const res = await data.json();
// setninja(res);
const fetchData = async () => {
const result = await axios(`https://jsonplaceholder.typicode.com/users/${id}`);
setninja(result.data);
};
fetchData();
console.log(data);
}, []);
I hope this info will be useful for you.
I Used Axios in getServerSideProps without any problems.
export const getServerSideProps: GetServerSideProps = async({
params,
res
}) => {
try {
const response = await axios.get(`/api/test`);
return {
props: {
data: response.data
},
}
} catch {
res.statusCode = 404;
return {
props: {}
};
}
};
Previously my apollo setup was listening to subscriptions until I added in socket.io and now my client setup is no longer listening to new data. my server codes seem to be ok based on my testing using graphql playground.
in my browser console, i get the following error message
client.js:652 WebSocket connection to 'ws://localhost:4000/' failed: Error during WebSocket handshake: Unexpected response code: 400
There seems to be some issue with my client side setup to use apollo subscriptions.
Appreciate any pointers? Thanks in advance
import { ApolloClient } from "apollo-client";
import { onError } from "apollo-link-error";
import { ApolloLink, split } from "apollo-link";
import { createUploadLink } from "apollo-upload-client";
import gql from "graphql-tag";
import { withClientState } from "apollo-link-state";
import { InMemoryCache } from "apollo-cache-inmemory";
import { WebSocketLink } from "apollo-link-ws";
import { getMainDefinition } from "apollo-utilities";
import { setContext } from "apollo-link-context";
const cache = new InMemoryCache();
const defaultState = {
currentGame: {
__typename: "currentGame",
teamAScore: 0,
teamBScore: 0,
teamAName: "EAGLES",
teamBName: "LOL"
}
};
const stateLink = withClientState({
cache,
defaults: defaultState,
resolvers: {
Mutation: {
updateGame: (_, { index, value }, { cache }) => {
const query = gql`
query GetCurrentGame {
currentGame #client {
teamAScore
teamBScore
teamAName
teamBName
}
}
`;
const previous = cache.readQuery({ query });
const data = {
currentGame: {
...previous.currentGame,
[index]: value
}
};
cache.writeQuery({ query, data });
return null;
},
resetCurrentGame: (_, d, { cache }) => {
cache.writeData({ data: defaultState });
}
}
}
});
const host = "http://localhost:4000";
// httpLink
const httpLink = createUploadLink({
uri: `${host}/graphql`,
credentials: "same-origin"
});
// wsLink
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/`,
options: {
reconnect: true
}
});
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const webLink = split(
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscription";
},
wsLink,
httpLink
);
// authMiddleware
const authLink = setContext(async (req, { headers }) => {
// const token = await AsyncStorage.getItem("#token");
const token = "";
return {
headers: {
...headers,
authorization: token ? `${token}` : ""
}
};
});
const errorLink = onError(({ networkError, graphQLErrors }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) console.log(`[Network error]: ${networkError}`);
});
export const client = new ApolloClient({
link: ApolloLink.from([authLink, stateLink, errorLink, webLink]),
cache
});
My server side code if required
//! Using Apollo Server Express
const app = express();
const path = "/graphql";
const schema = genSchema();
export const startServer = async () => {
const server = new ApolloServer({
schema,
context: ({ req }: any) => ({
req,
pubsub,
userLoader: userLoader()
})
});
app.use(cors());
app.use(authMiddleware);
app.use("/images", express.static("images"));
app.use(
"graphql",
graphqlUploadExpress({
uploadDir: "/",
maxFileSize: 100000000,
maxFiles: 10
}),
graphqlHTTP({ schema }) as any
);
server.applyMiddleware({ app, path });
//! Added Subscription Handler
const httpServer = createServer(app);
server.installSubscriptionHandlers(httpServer);
const port = process.env.PORT || 4000;
await createConnection();
await httpServer.listen({
port
});
console.log(`Server is running on localhost:${port}${server.graphqlPath}`);
};
Rectified. My client side apollo setup should point to ws://localhost:4000/graphql and not just ws://localhost:4000/
// wsLink
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true
}
});