I'm using Apollo Client as a graphql client on my next.js application, Here is the function that creates a client for me:
let client: ApolloClient<any>;
export const __ssrMode__: boolean = typeof window === "undefined";
export const uri: string = "http://localhost:3001/graphql";
const createApolloClient = (): ApolloClient<any> => {
return new ApolloClient({
credentials: "include",
ssrMode: __ssrMode__,
link: createHttpLink({
uri,
credentials: "include",
}),
cache: new InMemoryCache(),
});
};
Surprisingly, when I make a mutation to the graphql server I'm able to set the cookies but, I'm not able to get the cookies from the client. What may be possibily the problem?
I came to the same problem, my solution was to create a client every time a server-side rendering is made, maybe it's not ideal to have a client to execute GraphQL calls in the browser and others in the server but it's what worked best for me. This is the code:
import { ApolloClient, createHttpLink, InMemoryCache } from '#apollo/client';
import { NextPageContext } from 'next';
import { setContext } from '#apollo/client/link/context';
export const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
credentials: 'include',
});
const CreateClient = (ctx: NextPageContext | null) => {
const authLink = setContext((_, { headers }) => {
return {
headers: {
...headers,
cookie:
(typeof window === 'undefined'
? ctx?.req?.headers.cookie || undefined
: undefined) || '',
},
};
});
return new ApolloClient({
credentials: 'include',
link: authLink.concat(httpLink),
cache: new InMemoryCache(),
ssrMode: true,
});
};
export default CreateClient;
So, what I do is pass the context from the getServerSideProps and see if I have some cookies there, if so I just set the cookies, you can also send the authorization token if it's in the cookie. To call it is very simple:
export async function getServerSideProps(context: NextPageContext) {
const client = CreateClient(context);
const { data } = await client.query({
query: SOME_QUERY,
});
return {
props: {
data,
},
};
}
You can also do a HOC as in the Ben Awad tutorial Apollo Client HOC but I think it was too much for what I was trying to do. Hope it helped you or helps someone there :)
Also, I'm using Next 12.1.5 and React 18
Related
when put token value as hardcoded it's work properly,but when i got token from asyncstorage and pass token in headers i got this error
Response not sucessful:Recevied status code 500 What is wrong in mycode?
import { ApolloClient,ApolloProvider,InMemoryCache,gql } from '#apollo/client'
import { createUploadLink } from 'apollo-upload-client'
const getToken = async () => {
const token = await AsyncStorage.getItem('#storage_Key')
return token
}
const token = getToken()
const client = new ApolloClient({
link: createUploadLink({
uri: 'http://192.168.1.82:8080/graphql',
headers: {
authorization: token
}
}),
cache: new InMemoryCache()
})
When I add authorizan in headers I got this Error:Response not sucessful:Received status code 500 In React native otherwise it's work perfectly . What is issue here?
Can you try then function for async function
getToken().then((token) => {
const client = new ApolloClient({
link: createUploadLink({
uri: 'http://192.168.1.82:8080/graphql',
headers: {
authorization: token
}
}),
cache: new InMemoryCache()
});
});
I'm using VueJs3, Multer middleware for file uploading, expressJs. Its pretty new for me, so probably it is just a small mistake and i couldn't find it... After submit in console axios throw this error : "Request failed with status code 404".
And console.log(formData.append("file", this.file)) gives 'undefined'.
Some code from component:
import axios from "axios"
export default {
name: "FileUpload",
data() {
return {
file: "",
message: "",
}
},
methods: {
onSelect() {
const file = this.$refs.file.files[0]
this.file = file
},
async onSubmit() {
const formData = new FormData()
formData.append("file", this.file)
try {
await axios.post("/upload", formData)
this.message = "Upload successfully"
} catch (err) {
console.log(err)
this.message = "Something went wrong :("
}
},
},
}
And back-end:
const express = require("express")
const dotenv = require("dotenv")
const cors = require("cors")
const bodyParser = require("body-parser")
// const db = require("./app/config/db.config")
const multer = require("multer")
dotenv.config()
const app = express()
const port = process.env.PORT || 8080
const upload = multer({
dest: "./uploads",
})
var corsOptions = {
origin: "http://localhost:8081",
}
app.use(cors())
app.use(express.json())
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.post("/upload", upload.single("file"), (req, res) => {
res.json({ file: req.file })
})
app.listen(port, () => console.log(`Server started on port ${port}`))
I been trying to look for solutions in some tutorials, but probably mistake is to small for my rookie eyes at the moment...
Not sure which axios version you are using but you could try switching the versions because some versions handle multipart/form-data differently.
from docs
Starting from v0.27.0, Axios supports automatic object serialization to a FormData object if the request Content-Type header is set to multipart/form-data.
I take this to mean that the header needs to be defined explicitly
const formData = new FormData()
formData.append("file", this.file)
await axios.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
})
you can also try using the postForm method
docs
await axios.postForm("/upload", {file: this.file})
// or with multipart
await axios.postForm("/upload", {'files[]': [this.file]})
I would also recommend that you look at the network panel to see if that header is included, and try sending the request directly with something like postman or insomnia to determine definitively whether the issue is on the front-end or back-end.
I have a vue web app from which I'm trying to run a subscription using a hasura query.
My problem is that I cannot pass to the Websocket request an authorization token as the backend expects.
These are my current settings:
const token = localStorage.getItem("token") || null;
const options = {
httpUri: //graphql http entpoint,
wsUri: //graphql ws endpoint
};
let link = new HttpLink({
uri: options.httpUri
});
// Create the subscription websocket link if available
if (options.wsUri) {
const wsLink = new WebSocketLink(
new SubscriptionClient(options.wsUri, {
lazy: true,
reconnect: true,
connectionParams: {
headers: {
Authorization: `Bearer ${token}`
}
}
})
);
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
link
);
}
const authLink = setContext((_, { headers }) => {
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : ""
}
};
});
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.map(({ message }) => {
if (message.includes("unauthorized")) {
EventBus.$emit("unauthorized");
}
});
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const apolloClient = new ApolloClient({
link: ApolloLink.from([errorLink, authLink, link]),
cache: new InMemoryCache(),
connectToDevTools: true
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
When I try to run the subscription I get
HTTP Authentication failed; no valid credentials available
And in the ws request header I cannot see my Authorization bearer set.
A side info I need authorization for both http and ws requests
I think errorLink or authLink unexpectedly change websocket header token. You try modifying a bit:
const httpLink = from([
authLink,
// errorLink,
new HttpLink({
uri: Config.httpDataHost,
headers: {
[XHasuraClientName]: Config.hasuraClientName
}
})
]);
const wsLink = new WebSocketLink({ ... });
const link = ...
const apolloClient = new ApolloClient({
link: ApolloLink.from([errorLink, link]),
cache: new InMemoryCache(),
connectToDevTools: true
});
If it doesn't work, you can try commenting errorLink to check. Another thing is, you shouldn't get token globally, but use lazy function so ApolloClient can always get latest access token from local storage
const getIdToken = () => localStorage.getItem('token') || null;
const wsLink = new WebSocketLink({
uri: options.wsUri,
options: {
connectionParams: () => ({
headers: {
Authorization: getIdToken(),
}
}),
...
}
});
PS: I have an example repository with React + Apollo Client 3.0. Although you are using Vue.js,Apollo Client construction is the same https://github.com/hgiasac/ra-hasura-typescript-boilerplate/blob/auth-jwt/src/shared/ApolloClient.ts
Dear vue and apollo users;
I am dealing with the first time install problem.
When I first launch the app, I don't get results.
I am using ApolloClient, InMemoryCache, HttpLink from "apollo-boost"
I store my userID and JWT in ApplicationSettings(local storage)
How to set token dynamically?
Vue.use(VueApollo);
const httpLink = new HttpLink({
uri: "https://sebapi.com/graphql"
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from ApplicationSettings if it exists
var tokenInAppSettings = ApplicationSettings.getString("token");
// return the headers to the context so HTTP link can read them
return {
headers: {
...headers,
authorization: tokenInAppSettings
? `Bearer ${tokenInAppSettings}`
: null
}
};
});
export const apolloClient = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
const apolloProvider = new VueApollo({
defaultClient: apolloClient
});
I have created a GitHub repo reproducing problem
and a youtube video of the problem
There is no error during login but after navigating to the list page for the first time I got following errors...
JS: [Vue warn]: Invalid prop: type check failed for prop "items". Expected Array, Object, got Undefined
JS: Error sending the query 'birds' ServerError: Response not successful: Received status code 400
IT SEEMS APOLLO DOES NOT HAVE userID during first query.
NOTE: You can easily clear user data by using yarn cl script
# debug app without HMR
yarn devn
# clear user data of app
yarn cl
Solution repo using vuex:
https://github.com/kaanguru/data-firstlogin/tree/user-in-vuex
Move userID into vue instance
+welcome.vue+
//const userId = ApplicationSettings.getNumber("userID");
// I have moved userID into vue.
export default {
data() {
return {
birds:[],
bird:{
id: null,
isim: "",
bilezik: ""
},
userId: ApplicationSettings.getNumber("userID")
};
},
apollo: {
birds: {
query: gql`
query myBirds($userId: ID!) {
birds(where: { user: $userId }) {
id
isim
bilezik
}
}
`,
variables() {
return {
userId: this.userId,
};
},
},
},
};
How to pass Additional Header when calling mutation in React native apollo client ?
my Client is here:
import { HttpLink } from 'apollo-link-http';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
const makeApolloClient = (token) => {
// create an apollo link instance, a network interface for apollo client
const link = new HttpLink({
uri: 'http://x.x.x.x:xxxx/xxxx',
headers: {
Authorization: `Bearer ${token}`
},
});
// create an inmemory cache instance for caching graphql data
const cache = new InMemoryCache();
// instantiate apollo client with apollo link instance and cache instance
const client = new ApolloClient({
link,
cache
});
return client;
};
export default makeApolloClient;
If i need to add additional header to this same client when using query or mutation how can i do it ?
Is it possible with "apollo-link-context" ?
You haven't specified your React version however assuming you use Hooks you do it as follows. If you aren’t using hooks change the doc version for the links at the bottom of this answer using the drop down in the top left.
Where you have your query:
const GET_USER = gql`
query getUser{
node {
name
age
}
}
`;
You’ll want to run a query with the useQuery hook:
const { loading, error, data } = useQuery(GET_USER, {
context: {
headers: {
"Content-Type": "application/json"
}
}
})
Docs:
You can find the docs for each here:
- UseQuery: https://www.apollographql.com/docs/react/essentials/queries/
- Context Headers: https://www.apollographql.com/docs/link/links/http/#passing-context-per-query
This can be done by receiving the context which is set in mutation/query.
Setting Custom header in mutation
const [addTodo] = useMutation(ADD_TODO, {
refetchQueries: [{ query: GET_TODO }], //updating the list of todos list after adding
context: {
headers: {
"x-custom-component-add": "kkk-add",
"x-origin-server": "pure-react"
}
}
});
receiving context in middle ware which set in mutation/query
const httpLink = new HttpLink({ uri: "https://sxewr.sse.codesandbox.io/" });
const authMiddleware = new ApolloLink((operation, forward) => {
const customHeaders = operation.getContext().hasOwnProperty("headers") ? operation.getContext().headers : {};
console.log(customHeaders);
operation.setContext({
headers: {
...customHeaders
//we can also set the authorization header
// authorization: localStorage.getItem('jjjjjj'),
}
});
return forward(operation);
});
Finally passing the middleware in Apoolo Client
const client = new ApolloClient({
cache: new InMemoryCache(),
link: from([authMiddleware, httpLink])
});
Here is the working sample.
https://codesandbox.io/s/passing-custom-header-in-graphql-mutation-query-l332g?file=/src/index.js
Custom header look like this