Graphql token

I'm trying to use graphql (using urql) as my data provider and all was ok until I had to authenticate my requests. I need to use send a token (stored either in cookies or localstorage) in the headers but can't seem to make it work. My issue is that, even though the data provider setup is done in a "use client" file, this thing is rendered on the server first, and even though the queries happen on the client side it seems like the initial setup for the urql provider is done in the server. In the authExchange part of my gqlClient setup I even added something like this:
if (typeof localStorage !== 'undefined') {
token = await localStorage.getItem('token');
console.log('SETTING TOKEN', token);
}
if (typeof localStorage !== 'undefined') {
token = await localStorage.getItem('token');
console.log('SETTING TOKEN', token);
}
And I do see this console.log happening on the browser but the request never sends the header to the server. Here's my config for the gqlClient that is being used by Refine:
'use client';

// ... imports redacted

const gqlClient = new Client({
url: API_URL,
exchanges: [
fetchExchange,
authExchange(async (utils) => {
let token: null | string = null;

if (typeof localStorage !== 'undefined') {
token = await localStorage.getItem('token');
console.log('SETTING TOKEN', token);
}

return {
addAuthToOperation(operation) {
if (!token) return operation;

return utils.appendHeaders(operation, {
authorization: `Bearer ${token}`,
});
},
didAuthError(error) {
return error.graphQLErrors.some((e) => e.message === 'Unauthorized');
},
async refreshAuth() {},
};
}),
],
});

function convertOperationName(resource: string): string {
return `admin${camelCase(resource, { pascalCase: true })}`;
}

export const dataProvider = createDataProvider(gqlClient);
'use client';

// ... imports redacted

const gqlClient = new Client({
url: API_URL,
exchanges: [
fetchExchange,
authExchange(async (utils) => {
let token: null | string = null;

if (typeof localStorage !== 'undefined') {
token = await localStorage.getItem('token');
console.log('SETTING TOKEN', token);
}

return {
addAuthToOperation(operation) {
if (!token) return operation;

return utils.appendHeaders(operation, {
authorization: `Bearer ${token}`,
});
},
didAuthError(error) {
return error.graphQLErrors.some((e) => e.message === 'Unauthorized');
},
async refreshAuth() {},
};
}),
],
});

function convertOperationName(resource: string): string {
return `admin${camelCase(resource, { pascalCase: true })}`;
}

export const dataProvider = createDataProvider(gqlClient);
3 Replies
kapa.ai
kapa.ai4w ago
Hi @Rikas. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To address your issue with authenticating GraphQL requests using urql in Refine, I can provide some guidance based on the available knowledge sources. The main challenge you're facing is that the initial setup for the urql provider is happening on the server, but you need to include authentication tokens that are only available on the client side. Here's how you can approach this: 1. Create a custom GraphQL client: Instead of creating the GraphQL client directly, you can create a function that returns the client. This function can be called on the client side, where you have access to localStorage.
import { Client, fetchExchange } from "@urql/core";
import { authExchange } from "@urql/exchange-auth";
export const createGraphQLClient = () => {
return new Client({
url: API_URL,
exchanges: [
authExchange({
getAuth: async () => {
const token = localStorage.getItem("token");
if (token) {
return { token };
}
return null;
},
addAuthToOperation: ({ operation, authState }) => {
if (!authState || !authState.token) {
return operation;
}
const fetchOptions =
typeof operation.context.fetchOptions === "function"
? operation.context.fetchOptions()
: operation.context.fetchOptions || {};
return {
...operation,
context: {
...operation.context,
fetchOptions: {
...fetchOptions,
headers: {
...fetchOptions.headers,
Authorization: `Bearer ${authState.token}`,
},
},
},
};
},
didAuthError: ({ error }) => {
return error.graphQLErrors.some(e => e.extensions?.code === 'FORBIDDEN');
},
}),
fetchExchange,
],
});
};
import { Client, fetchExchange } from "@urql/core";
import { authExchange } from "@urql/exchange-auth";
export const createGraphQLClient = () => {
return new Client({
url: API_URL,
exchanges: [
authExchange({
getAuth: async () => {
const token = localStorage.getItem("token");
if (token) {
return { token };
}
return null;
},
addAuthToOperation: ({ operation, authState }) => {
if (!authState || !authState.token) {
return operation;
}
const fetchOptions =
typeof operation.context.fetchOptions === "function"
? operation.context.fetchOptions()
: operation.context.fetchOptions || {};
return {
...operation,
context: {
...operation.context,
fetchOptions: {
...fetchOptions,
headers: {
...fetchOptions.headers,
Authorization: `Bearer ${authState.token}`,
},
},
},
};
},
didAuthError: ({ error }) => {
return error.graphQLErrors.some(e => e.extensions?.code === 'FORBIDDEN');
},
}),
fetchExchange,
],
});
};
2. Use the custom client in your data provider: In your data provider setup, use the createGraphQLClient function instead of directly creating the client:
'use client';

import { createGraphQLClient } from './graphqlClient';
import { createDataProvider } from "@refinedev/graphql";

const dataProvider = createDataProvider(createGraphQLClient());

export { dataProvider };
'use client';

import { createGraphQLClient } from './graphqlClient';
import { createDataProvider } from "@refinedev/graphql";

const dataProvider = createDataProvider(createGraphQLClient());

export { dataProvider };
3. Update your Refine setup: In your Refine setup (usually in App.tsx or equivalent), make sure you're using the data provider correctly:
import { Refine } from "@refinedev/core";
import { dataProvider } from "./dataProvider";
function App() {
return (
<Refine
dataProvider={dataProvider}
// ... other Refine props
>
{/* ... */}
</Refine>
);
}
import { Refine } from "@refinedev/core";
import { dataProvider } from "./dataProvider";
function App() {
return (
<Refine
dataProvider={dataProvider}
// ... other Refine props
>
{/* ... */}
</Refine>
);
}
This approach ensures that the GraphQL client is created on the client side, where it has access to localStorage. The authentication token is then added to each request as needed. For more details on authentication with Refine and GraphQL, you can refer to the Refine GraphQL Authentication documentation (https://refine.dev/docs/data/packages/graphql/#authentication). If you're still facing issues, you might want to check your authProvider implementation to ensure it's correctly storing and retrieving the token. The Refine Authentication Guide (https://refine.dev/docs/guides-concepts/authentication/) provides more information on setting up authentication in Refine. Remember to handle token refreshing and error cases appropriately in your production application. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai4w ago
<@99162486523060224> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
provincial-silver
provincial-silverOP4w ago
It worked!

Did you find this page helpful?