Using Apollo in react that's working on computer but it's not working on mobile browser, I have seen many solutions on here and github but they are all for version 2 or less, I'm using "#apollo/client": "^3.5.8" and here's where the occurs
const getSignedUrl = async (file) => {
try {
const api = await mutationFunc({ variables: {
fileName: file[0].name,
email: currentUser.email
}})
.....
} catch (error) {
throw new Error('Unknown error')
}
}
It does not even connect to the server, I just get Failed to fetch error.
According to the deprecated solutions, it has something to do with ApolloClient constructor, so here's my index file:
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import {
ApolloClient,
InMemoryCache,
ApolloProvider,
HttpLink,
createNetworkInterface
} from "#apollo/client";
// const client = new ApolloClient({
// link: new HttpLink({
// uri: Platform.select({
// ios: 'http://localhost:80/',
// android: 'http://10.0.2.2:80/',
// }),
// }),
// cache: new InMemoryCache(),
// });
// const networkInterface = createNetworkInterface('/graphql')
const client = new ApolloClient({
// uri: 'http://localhost:8000/',
link: new HttpLink({ uri: 'http://localhost:8000/' }),
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />,
</ApolloProvider>,
document.getElementById("root")
);
EDIT
The API lives on my PC, so I tried changing the ApolloClient constructor uri to
const client = new ApolloClient({
link: new HttpLink({ uri: http://192.168.1.101:8000' }),
cache: new InMemoryCache()
});
I can also query the API directly on my phone using http://192.168.1.101:8000: Here is the query:
Here is the response from the API
and Here is the failed mutation on React side:
NOTE: I'm not uploading the image just send the name to get a pre-signed s3 url. But it's not even sending to the api.
Where does your GraphQL server run? I assume it doesn't run on the mobile device. If so, you have to specify the URL of the server in Apollo client constructor.
http://localhost:8000/ --> http://myserver:8000/
And of course your mobile device should have access to port 8000 of myserver.
Related
I have a React/Next.js app which is using GraphQL and Apollo to connect and interact with a headless API. I am using Strapi as my headless API/CMS which is working great, except for one issue. I am trying to upload a file from my React app to a content type in my Strapi CMS using a GraphQL mutation and it keeps failing on the upload part.
When I upload a file using Altair(my playground environment) to Strapi with the exact same mutation everything works fine, but once I try to run the same mutation from my React app I get this error:
Variable \"$file\" got invalid value {}; Upload value invalid.. Everything I see online brings up using apollo-upload-client and adding something like link: createUploadLink({ uri: "http://localhost:4300/graphql" }), to my Apollo client initiation. I have tried that but whenever I use it, it breaks my app and I get this GraphQL error Error: Cannot return null for non-nullable field UsersPermissionsMe.username..
It seems like the only answer I can find is using apollo-upload-client but when I use it, it seems to break my app. I don't know if I need to use it differently maybe because I am using Strapi, or Next, or #apollo/client. I am a little lost on this one.
This is how I initiate my Apollo client and when everything works except uploading a file.
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from "#apollo/client";
import { parseCookies } from "nookies";
import { useMemo } from 'react'
import getConfig from "next/config";
const { publicRuntimeConfig } = getConfig();
let apolloClient: ApolloClient<NormalizedCacheObject>;
function createApolloClient(ctx) {
return new ApolloClient({
ssrMode: typeof window === "undefined",
uri: publicRuntimeConfig.PUBLIC_GRAPHQL_API_URL,
cache: new InMemoryCache(),
headers: {
Authorization: `Bearer ${ctx ? parseCookies(ctx).jwt : parseCookies().jwt}`,
},
});
}
My app runs perfect with this code except for the fact that I get "message":"Variable \"$file\" got invalid value {}; Upload value invalid.", whenever I try to upload a file. So when I try to use apollo-upload-client to fix that like this:
import { ApolloClient, InMemoryCache, NormalizedCacheObject } from "#apollo/client";
import { createUploadLink } from 'apollo-upload-client';
import { parseCookies } from "nookies";
import { useMemo } from 'react'
import getConfig from "next/config";
const { publicRuntimeConfig } = getConfig();
let apolloClient: ApolloClient<NormalizedCacheObject>;
function createApolloClient(ctx) {
return new ApolloClient({
ssrMode: typeof window === "undefined",
// uri: publicRuntimeConfig.PUBLIC_GRAPHQL_API_URL,
link: createUploadLink({ uri: publicRuntimeConfig.PUBLIC_GRAPHQL_API_URL }),
cache: new InMemoryCache(),
headers: {
Authorization: `Bearer ${ctx ? parseCookies(ctx).jwt : parseCookies().jwt}`,
},
});
}
I get this error: Error: Cannot return null for non-nullable field UsersPermissionsMe.username..
I am still new to GraphQL and Strapi so maybe I am missing something obvious. Everything works except uploads. I can upload from my playground, just not from my app, that is where I am at.
So after two days of this issue, I figured it out after 10 minutes of reading the apollo-upload-client documentation. So much shame. All I needed to do was move my header request from the ApolloClient options to the createUploadLink options. Final fix was
function createApolloClient(ctx) {
return new ApolloClient({
ssrMode: typeof window === "undefined",
link: createUploadLink({
uri: publicRuntimeConfig.PUBLIC_GRAPHQL_API_URL,
headers: {
Authorization: `Bearer ${ctx ? parseCookies(ctx).jwt : parseCookies().jwt}`,
},
}),
cache: new InMemoryCache(),
});
}
I'm trying to deploy my application to a production environment, but having some trouble wiring it all together.
I've got a create-react-app for my frontend, which is served up by a simple express/serve server. On the backend, I've got NGINX proxying successfully to my API server, which is using Apollo to serve my data. The API is running on port 4000.
The Apollo-Server is as-follows, and works fine:
import { resolve } from "path";
import dotenv from "dotenv";
const envi = process.env.NODE_ENV;
dotenv.config({ path: resolve(__dirname, `../.${envi}.env`) });
import "reflect-metadata";
import { connect } from "./mongodb/connect";
import { buildSchema } from "type-graphql";
import { ApolloServer } from "apollo-server";
import { SenateCommitteeResolver, HouseCommitteeResolver } from "./resolvers";
import { populateDatabase } from "./util";
(async () => {
// Connect to MongoDB
await connect();
console.log(`📊 Databases connected`);
const schema = await buildSchema({
resolvers: [HouseCommitteeResolver, SenateCommitteeResolver],
emitSchemaFile: resolve(__dirname, "schema.gql"),
});
// If development, set database docs
envi === "development" && (await populateDatabase());
// Launch the server!
const server = new ApolloServer({
schema,
playground: true,
});
// Server listens at URL
const { url } = await server.listen(4000);
console.log(`🚀 Server ready, at ${url}`);
})();
I'm trying to connect my express server to the Apollo Server, but that's where I'm running into problems. The application is supposed to connect using Apollo's Client and HTTP Link, because I'm using Apollo Client on the frontend too:
import React, { useEffect } from "react";
import { AppRouter } from "./routers";
import ReactGA from "react-ga";
import { ApolloProvider } from "#apollo/client";
import client from "./graphql/client";
import "./styles/index.scss";
function App(): React.ReactElement {
return (
<ApolloProvider client={client}>
<AppRouter />
</ApolloProvider>
);
}
export default App;
And here's the client file:
import { ApolloClient, InMemoryCache, createHttpLink } from "#apollo/client";
const httpLink = createHttpLink({ uri: process.env.REACT_APP_API as string });
const cache = new InMemoryCache();
const client = new ApolloClient({
link: httpLink,
cache,
connectToDevTools: true,
});
export default client;
However, when the user navigates to the site and the site itself tries to make a request to my backend, I'm getting a CORS error:
Access to fetch at 'https://www.cloture.app/' from origin 'https://cloture.app' 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.
What's going wrong? How can I connect Apollo's client with my Apollo Server on the backend?
Adding it here, because the suggestion requires some code.
Try adding :
const server = new ApolloServer({
schema,
playground: true,
cors: {
origin: "*" // it will allow any client to access the server, but you can add specific url also
}
});
I have a web app using aws appsync as backend and react + apollo client (v3) as front end. But when I try connecting apollo client to appsync, I get an error message from the library:
./node_modules/aws-appsync-react/lib/offline-helpers.js
Module not found: Can't resolve 'react-apollo' in '/Users/mypath/web/node_modules/aws-appsync-react/lib'
Here's the config for the client:
import AWSAppSyncClient from "aws-appsync";
import AppSyncConfig from "./aws-exports";
export const apolloClient = new AWSAppSyncClient({
url: AppSyncConfig.aws_appsync_graphqlEndpoint,
region: AppSyncConfig.aws_appsync_region,
auth: {
type: AppSyncConfig.aws_appsync_authenticationType,
apiKey: AppSyncConfig.aws_appsync_apiKey,
},
});
And the in my App.ts:
import { ApolloProvider } from "#apollo/client";
import { Rehydrated } from "aws-appsync-react";
import { apolloClient } from "./apollo";
...
<ApolloProvider client={apolloClient}>
<Rehydrated>
<MyApp />
</Rehydrated>
</ApolloProvider>
Looks like a compatibility issue?
I'm using "#apollo/client": "^3.1.3", "aws-appsync": "^4.0.0","aws-appsync-react": "^4.0.0",.
It is a compatibility issue. Current version of aws-appsync doesn't support apollo-client v3, see this thread for progress:
https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/448
Best workaround is this: Proper way to setup AWSAppSyncClient, Apollo & React
Note the workaround does use two deprecated libraries but can be slightly improved as:
import { ApolloClient, ApolloLink, InMemoryCache } from "#apollo/client";
import { createAuthLink } from "aws-appsync-auth-link";
import { createHttpLink } from "apollo-link-http";
import AppSyncConfig from "./aws-exports";
const url = AppSyncConfig.aws_appsync_graphqlEndpoint;
const region = AppSyncConfig.aws_project_region;
const auth = {
type: AppSyncConfig.aws_appsync_authenticationType,
apiKey: AppSyncConfig.aws_appsync_apiKey,
};
const link = ApolloLink.from([
// #ts-ignore
createAuthLink({ url, region, auth }),
// #ts-ignore
createHttpLink({ uri: url }),
]);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
export default client;
I finally finished my backend with apollo.
Now I moved to the frontend.
I made it work with the normal http connection and fetched some messages from the server (chat app).
Now I also tried to connect the subscription link to it (which was very confusing in itself because there are a few examples outside and everybody does it a bit differently, but neither worked).
So here is how my index file looks on react frontend:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { ApolloClient } from "apollo-client";
import { ApolloProvider } from "react-apollo";
import { WebSocketLink } from "apollo-link-ws";
import { ApolloLink } from "apollo-link";
import { HttpLink } from "apollo-link-http";
import { InMemoryCache } from "apollo-cache-inmemory";
const wsLink = new WebSocketLink({
uri: `ws://localhost:4000/graphql`,
options: {
reconnect: true
}
});
// Create an http link:
const httpLink = new HttpLink({
uri: "http://localhost:4000/graphql"
});
const link = ApolloLink.from([httpLink, wsLink]);
const client = new ApolloClient({
link,
cache: new InMemoryCache()
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
serviceWorker.unregister();
I also tried different methods for example with split from apollo-link like this:
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
I also tried it with ApolloClient from apollo-boost instead of apollo-client.
Can somebody please help me out, because I can't get it to work.
Error message is always:
WebSocket connection to 'ws://localhost:4000/graphql' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
If you want to see the full code: https://github.com/SelfDevTV/graphql-simple-chat
I am trying to implement the Authentication workflow officially described in the Apollo docs as I did before in other projects, but this time using Gatsby.
The idea seems pretty straightforward. Need to create/update gatsby-browser.js like when using redux but to initialise the ApolloClient and pass through ApolloProvider.
Something like:
import React from 'react'
import { Router } from 'react-router-dom'
import { ApolloProvider } from 'react-apollo'
import { ApolloClient } from 'apollo-client'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { setContext } from 'apollo-link-context'
const httpLink = createHttpLink({
uri: process.env.ENDPOINT_URI,
credentials: 'same-origin',
})
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token');
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : "",
}
}
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache().restore({}),
})
exports.replaceRouterComponent = ({ history }) => {
const ConnectedRouterWrapper = ({ children }) => (
<ApolloProvider client={client}>
<Router history={history}>{children}</Router>
</ApolloProvider>
)
return ConnectedRouterWrapper
}
Then login using
<Mutation
mutation={LOGIN_MUTATION}
refetchQueries={[{ query: CURRENT_USER_QUERY }]}
>
{/*...*/}
</Mutation>
and in another Component with something like the Status (where, if logged in shows x otherwise y):
<Query query={CURRENT_USER_QUERY} fetchPolicy="network-only">
{/*...*/}
</Query>
The problem
With this configuration, when the login receives data (so, credentials are ok), I save the token received in localStorage but the Status component do the Query with the previous (empty) token, so shows logged out, also doing something like history.push('/') after login.
Refreshing the page, since the item in localStorage was saved before, the Status shows as logged in.
Expected behaviour
The idea is to have the Status component updated after login avoiding doing a refresh of the page, for this reason I added refetchQueries={[{ query: CURRENT_USER_QUERY }]}.
I tried some things like pass props (no needed in case I change the route and use withRouter in the Status component), but seems to late, the Query was fired without the new token.
The main issue here is probably your Status component needs to be re-render only after the the token saved into localStorage. Right now it's probably re-render too early.
You can try wrapping your Status component with another wrapper component, and check for the token existence before rendering the Status component.
I believe your issue is related to how the links are defined. IMO, the AuthLink should be defined before the HttpLink (as middleware):
const client = new ApolloClient({
link: ApolloLink.from([
authLink,
httpLink
]),
cache: new InMemoryCache().restore({}),
})
While the documentation says that concat and from do the same thing, order is important. You want to have the context set before sending the request.