I want to fetch XML with Apollo.
I am using https://www.w3schools.com/xml/note.xml as example
import { RestLink } from "apollo-link-rest";
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
const { parseString } = require("xml2js");
const parseXmlResponseToJson = (xml) => {
return parseString(xml, (err, result) => {
console.log(result);
// {note: {to: Array(1), from: Array(1), heading: Array(1), body: Array(1)}}
return JSON.stringify(result);
});
};
const restLink = new RestLink({
uri: "https://www.w3schools.com",
responseTransformer: async (response) =>
response.text().then((text) => parseXmlResponseToJson(text)),
});
export const client = new ApolloClient({
link: restLink,
cache: new InMemoryCache(),
});
And my query
import gql from "graphql-tag";
export const GET_CDS = gql`
query MyQuery {
cds #rest(type: "note", path: "/xml/note.xml") {
note {
to
}
}
}
`;
Now, when I try to use the data in my React component
function Component() {
const { loading, error, data } = useQuery(GET_PRODUCTS);
console.log('data', data.cds)
...
I get this
Related
My problem is that I cannot see any date I try to fetch.
component.js
render(){
return (
<Query query={QueryCategories}>
{({ data }) => {
console.log("data", data)
return(
data.map((m)=> <p>{m}</p>)
)
}}
</Query>)}
query.js
import { gql } from "#apollo/client";
export const QueryCategories = gql`
query getCategories {
categories {
name
}
}
`;
index.js
import { ApolloClient, InMemoryCache } from '#apollo/client';
const cache = new InMemoryCache({
typePolicies: {
AttributeSet: {
keyFields: false,
},
Attribute: {
keyFields: false,
},
},
});
const client = new ApolloClient({
uri: 'http://localhost:4000',
cache,
});
My main goal is give it as props to another component and connect to redux
I am new to NextJS. I have a page that needs to display real-time data pulled from a Hasura GraphQL backend.
In other non-NextJS apps, I have used GraphQL subscriptions with the Apollo client library. Under the hood, this uses websockets.
I can get GraphQL working in NextJS when it's not using subscriptions. I'm pretty sure this is running on the server-side:
import React from "react";
import { AppProps } from "next/app";
import withApollo from 'next-with-apollo';
import { ApolloProvider } from '#apollo/react-hooks';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
export default withApollo(({ initialState }) => new ApolloClient({
uri: "https://my_hasura_instance.com/v1/graphql",
cache: new InMemoryCache().restore(initialState || {}),
request: (operation: any) => {
const token = getToken();
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
}
}))(App);
And I use it this way:
import { useQuery } from '#apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
query {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useQuery(myQuery);
return <p>{JSON.stringify(data)}</p>
}
However, I would instead like to do this:
import { useSubscription } from '#apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
subscription {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useSubscription(myQuery);
return <p>{JSON.stringify(data)}</p>
}
What I've tried
I've tried splitting the HttpLink and WebsocketLink elements in the ApolloClient, like so:
import React from "react";
import { AppProps } from "next/app";
import { ApolloProvider } from '#apollo/react-hooks';
import withApollo from 'next-with-apollo';
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";
import { split } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
const wsLink = new WebSocketLink({
uri: "wss://my_hasura_instance.com/v1/graphql",
options: {
reconnect: true,
timeout: 10000,
connectionParams: () => ({
headers: {
authorization: getToken() ? `Bearer ${getToken()}` : ""
}
})
},
});
const httpLink = new HttpLink({
uri: "https://hasura-g3uc.onrender.com/v1/graphql",
});
const link = process.browser ? split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
) : httpLink;
export default withApollo(({ initialState }) => new ApolloClient({
link: link,
cache: new InMemoryCache().restore(initialState || {}),
}))(App);
But when I load the page, I get an Internal Server Error, and this error in the terminal:
Error: Unable to find native implementation, or alternative implementation for WebSocket!
It seems to me that the ApolloClient is then being generated on the server-side, where there is no WebSocket implementation. How can I make this happen on the client-side?
Found workaround to make it work, take look at this answer https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-359261024
the reason was due to server-side rendering; these statements must run in the browser, so we test if we have process.browser !!
relevant section from the attached github link:
const wsLink = process.browser ? new WebSocketLink({ // if you instantiate in the server, the error will be thrown
uri: `ws://localhost:4000/subscriptions`,
options: {
reconnect: true
}
}) : null;
const httplink = new HttpLink({
uri: 'http://localhost:3000/graphql',
credentials: 'same-origin'
});
const link = process.browser ? split( //only create the split in the browser
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httplink,
) : httplink;
This answer seems to be more actual
https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-775578327
You should install ws by npm i ws and add webSocketImpl: ws to WebSocketLink argument.
import ws from 'ws';
const wsLink = new WebSocketLink({
uri: endpoints.ws,
options: {
reconnect: true,
connectionParams: () => ({
...getToken() && {Authorization: getToken()}
})
},
webSocketImpl: ws
});
Solution: Make wsLink a function variable like the code below.
// src/apollo.ts
import { ApolloClient, HttpLink, InMemoryCache } from "#apollo/client";
import { GraphQLWsLink } from "#apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql'
});
const wsLink = () => {
return new GraphQLWsLink(createClient({
url: 'ws://localhost:3000/graphql'
}));
}
export const apolloClient = new ApolloClient({
link: typeof window === 'undefined' ? httpLink : wsLink(),
cache: new InMemoryCache(),
});
// pages/_app.tsx
import { ApolloProvider } from "#apollo/client";
import { apolloClient } from "../src/apollo";
function MyApp({ Component, pageProps }) {
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
);
}
Here is my problem, in my react app whenever a order is created I want to get a Subscription for that order called orderNotification,
setup in order resolver:
Subscription: {
orderNotification: {
subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(ORDER_NOTIFICATION)
}
}
mutation:
Mutation: {
async createOrder(_, { MakeOrderInput: { state, message, products, total } }, context) {
try {
const userAuth = isAuth(context);
const pubsub = context.pubsub;
const newOrder = new Order({
state,
username: userAuth.username,
user: userAuth.id,
createdAt: new Date().toISOString(),
total,
message,
products,
userAddress: userAuth.address,
});
const index = products.findIndex(x => x.cost === 0);
if (index != -1) {
const u = await User.findById({ _id: userAuth.id });
await User.findByIdAndUpdate({ _id: userAuth.id }, { points: u.points - 20 }, (err, data) => {
if (err) {
console.log(err)
} else {
console.log('fatto')
}
});
}
const order = await newOrder.save();
pubsub.publish(ORDER_NOTIFICATION, {
orderNotification: order
});
return order;
} catch (err) {
// throw new Error(err);
console.log(err)
}
},
all works fine in graphql Playground but when I have to get and show the results in my component the returned data is null:
import React from 'react'
import gql from 'graphql-tag';
import { useSubscription } from '#apollo/client';
import { Box } from 'grommet'
function SubscriptionOrder() {
const { data, loading, error } = useSubscription(SUBSCRIPTION_USER_ORDER, {
onSubscriptionData: (d) => console.log(d),
onSubscriptionComplete: (da) => console.log(da)
});
// return null
// console.log(data)
return (
<>
<Box style={{ marginTop: '96px' }}>
{data && data.orderNotification ? (
<h1>hi: {data.orderNotification.username}</h1>
) : (
<h1>NO DATA</h1>
)
}
</Box>
</>
)
};
const SUBSCRIPTION_USER_ORDER = gql`
subscription orderNotification{
orderNotification {
username
}
}
`;
export default SubscriptionOrder;
so considering that in playground works the error may be in my ApolloClient links configuration:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from '#apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloLink, Observable, split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { TokenRefreshLink } from "apollo-link-token-refresh";
import jwtDecode from "jwt-decode";
import { getAccessToken, setAccessToken } from './accessToken';
import dotenv from 'dotenv/config.js'
const cache = new InMemoryCache({});
const httpLink = new HttpLink({
uri: process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_SERVER_DEV}/graphql` : `${process.env.REACT_APP_SERVER_PRODUCTION}/graphql`,
credentials: "include",
});
const wsLink = new WebSocketLink({
uri: process.env.NODE_ENV === 'development' ? `ws://${process.env.REACT_APP_SERVER_DEV_WS}/graphql` : `ws://${process.env.REACT_APP_SERVER_PRODUCTION_WS}/graphql`,
options: {
reconnect: true,
lazy: true,
inactivityTimeout: 1000,
},
connectionCallback: err => {
if (err) {
console.log('Error Connecting to Subscriptions Server', err);
}
}
});
const splitLink = split(
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === "OperationDefinition" && operation === "subscriptions";
},
wsLink,
httpLink
);
const requestLink = new ApolloLink(
(operation, forward) =>
new Observable(observer => {
let handle;
Promise.resolve(operation)
.then(operation => {
const accessToken = getAccessToken();
if (accessToken) {
operation.setContext({
headers: {
authorization: `Bearer ${accessToken}`
},
fetchOptions: {
credentials: 'include'
}
});
}
})
.then(() => {
handle = forward(operation).subscribe({
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer)
});
})
.catch(observer.error.bind(observer));
return () => {
if (handle) handle.unsubscribe();
};
})
);
const client = new ApolloClient({
link: ApolloLink.from([
new TokenRefreshLink({
accessTokenField: "accessToken",
isTokenValidOrUndefined: () => {
const token = getAccessToken();
if (!token) {
return true;
}
try {
const { exp } = jwtDecode(token);
if (Date.now() >= exp * 1000) {
return false;
} else {
return true;
}
} catch {
return false;
}
},
fetchAccessToken: () => {
return fetch(process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_SERVER_DEV}/refresh_token` : `${process.env.REACT_APP_SERVER_PRODUCTION}/refresh_token`, {
method: "POST",
credentials: "include"
});
},
handleFetch: accessToken => {
setAccessToken(accessToken);
},
handleError: err => {
console.warn("Your refresh token is invalid. Try to relogin");
console.error(err);
}
}),
onError(({ graphQLErrors, networkError }) => {
console.log(graphQLErrors);
console.log(networkError);
}),
requestLink,
splitLink,
]),
cache,
connectToDevTools: true,
credentials: 'include',
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode >,
document.getElementById('root')
);
here is my server:
import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req, res }) => ({ req, res, pubsub }),
introspection: true,
cors: corsOptions,
});
server.applyMiddleware({ app, cors: false });
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
httpServer.listen(PORT, () => {
console.log(`🚀 Server ready at http://localhost:${PORT}${server.graphqlPath}`)
console.log(`🚀 Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})
resolve the payload in the subscription with
Subscription: {
orderNotification: {
subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(ORDER_NOTIFICATION),
resolve: (payload) => {
return payload;
},
}
}
another way to remove null or unwanted entries is withFilter to remove intimation in case of unknown or unwanted events;
writing this answer as an opyion, but the answer above is better
const { withFilter } = require('graphql-subscriptions');
Subscription: {
orderNotification: {
subscribe: withFilter(
() => pubsub.asyncIterator('ORDER_NOTIFICATION'),
(payload, variables) => {
// add any condition here
return (payload && payload !== null);
},
),
},
}
I am new to NextJS. I have a page that needs to display real-time data pulled from a Hasura GraphQL backend.
In other non-NextJS apps, I have used GraphQL subscriptions with the Apollo client library. Under the hood, this uses websockets.
I can get GraphQL working in NextJS when it's not using subscriptions. I'm pretty sure this is running on the server-side:
import React from "react";
import { AppProps } from "next/app";
import withApollo from 'next-with-apollo';
import { ApolloProvider } from '#apollo/react-hooks';
import ApolloClient, { InMemoryCache } from 'apollo-boost';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
export default withApollo(({ initialState }) => new ApolloClient({
uri: "https://my_hasura_instance.com/v1/graphql",
cache: new InMemoryCache().restore(initialState || {}),
request: (operation: any) => {
const token = getToken();
operation.setContext({
headers: {
authorization: token ? `Bearer ${token}` : ''
}
});
}
}))(App);
And I use it this way:
import { useQuery } from '#apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
query {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useQuery(myQuery);
return <p>{JSON.stringify(data)}</p>
}
However, I would instead like to do this:
import { useSubscription } from '#apollo/react-hooks';
import { gql } from 'apollo-boost';
const myQuery = gql`
subscription {
...
}
`;
const MyComponent: React.FC = () => {
const { data } = useSubscription(myQuery);
return <p>{JSON.stringify(data)}</p>
}
What I've tried
I've tried splitting the HttpLink and WebsocketLink elements in the ApolloClient, like so:
import React from "react";
import { AppProps } from "next/app";
import { ApolloProvider } from '#apollo/react-hooks';
import withApollo from 'next-with-apollo';
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";
import { split } from 'apollo-link';
import { HttpLink } from 'apollo-link-http';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { getToken } from "../util/auth";
interface Props extends AppProps {
apollo: any
}
const App: React.FC<Props> = ({ Component, pageProps, apollo }) => (
<ApolloProvider client={apollo}>
<Component {...pageProps}/>
</ApolloProvider>
);
const wsLink = new WebSocketLink({
uri: "wss://my_hasura_instance.com/v1/graphql",
options: {
reconnect: true,
timeout: 10000,
connectionParams: () => ({
headers: {
authorization: getToken() ? `Bearer ${getToken()}` : ""
}
})
},
});
const httpLink = new HttpLink({
uri: "https://hasura-g3uc.onrender.com/v1/graphql",
});
const link = process.browser ? split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink
) : httpLink;
export default withApollo(({ initialState }) => new ApolloClient({
link: link,
cache: new InMemoryCache().restore(initialState || {}),
}))(App);
But when I load the page, I get an Internal Server Error, and this error in the terminal:
Error: Unable to find native implementation, or alternative implementation for WebSocket!
It seems to me that the ApolloClient is then being generated on the server-side, where there is no WebSocket implementation. How can I make this happen on the client-side?
Found workaround to make it work, take look at this answer https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-359261024
the reason was due to server-side rendering; these statements must run in the browser, so we test if we have process.browser !!
relevant section from the attached github link:
const wsLink = process.browser ? new WebSocketLink({ // if you instantiate in the server, the error will be thrown
uri: `ws://localhost:4000/subscriptions`,
options: {
reconnect: true
}
}) : null;
const httplink = new HttpLink({
uri: 'http://localhost:3000/graphql',
credentials: 'same-origin'
});
const link = process.browser ? split( //only create the split in the browser
// split based on operation type
({ query }) => {
const { kind, operation } = getMainDefinition(query);
return kind === 'OperationDefinition' && operation === 'subscription';
},
wsLink,
httplink,
) : httplink;
This answer seems to be more actual
https://github.com/apollographql/subscriptions-transport-ws/issues/333#issuecomment-775578327
You should install ws by npm i ws and add webSocketImpl: ws to WebSocketLink argument.
import ws from 'ws';
const wsLink = new WebSocketLink({
uri: endpoints.ws,
options: {
reconnect: true,
connectionParams: () => ({
...getToken() && {Authorization: getToken()}
})
},
webSocketImpl: ws
});
Solution: Make wsLink a function variable like the code below.
// src/apollo.ts
import { ApolloClient, HttpLink, InMemoryCache } from "#apollo/client";
import { GraphQLWsLink } from "#apollo/client/link/subscriptions";
import { createClient } from "graphql-ws";
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql'
});
const wsLink = () => {
return new GraphQLWsLink(createClient({
url: 'ws://localhost:3000/graphql'
}));
}
export const apolloClient = new ApolloClient({
link: typeof window === 'undefined' ? httpLink : wsLink(),
cache: new InMemoryCache(),
});
// pages/_app.tsx
import { ApolloProvider } from "#apollo/client";
import { apolloClient } from "../src/apollo";
function MyApp({ Component, pageProps }) {
return (
<ApolloProvider client={apolloClient}>
<Component {...pageProps} />
</ApolloProvider>
);
}
I am trying to implement react-apollo to store my local states, I can write to them using client.writeData() but when i try to access them, I keep getting errors
I tried following https://github.com/wesbos/Advanced-React/tree/master/finished-application/frontend. I tried implementing apollo-link-state as well but keeps throwing error.
withData.js
import withApollo from "next-with-apollo";
import { ApolloClient } from "apollo-client";
import { createHttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
const cache = new InMemoryCache();
function createClient({ headers }) {
return new ApolloClient({
cache,
link: new createHttpLink({
uri: "http://localhost:4000/",
request: operation => {
operation.setContext({
fetchOptions: {
credentials: "same-origin"
},
headers: {
"Content-Type": "application/json"
}
});
}
}),
clientState: {
resolvers: {
Query: {
getLocalData: (_, { text }, { cache }) => {
const query = gql`
query getLocalData {
loading #client
}
`;
const previous = cache.readQuery({ query });
return previous.loading;
}
},
Muatation: {
toggleLoading: (_, { text }, { cache }) => {
const query = gql`
query getLocalData {
loading #client
}
`;
var previous = cache.readQuery({ query });
cache.writeData({ data: { loading: !previous.loading } });
return null;
}
}
},
defaults: {
loading: true,
},
typeDefs: {}
}
});
}
export default withApollo(createClient);
_app.js
import App, { Container } from 'next/app';
import Page from '../components/Page';
import { ApolloProvider } from 'react-apollo';
import withData from '../lib/withData';
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
// this exposes the query to the user
pageProps.query = ctx.query;
return { pageProps };
}
render() {
const { Component, apollo, pageProps } = this.props;
return (
<Container>
<ApolloProvider client={apollo}>
<Page>
<Component {...pageProps} />
</Page>
</ApolloProvider>
</Container>
);
}
}
export default withData(MyApp);
I expect to run efficiently
but the actual output is: Could not find "client" in the context of ApolloConsumer. Wrap the root component in an