3

The Github GraphQL v4 API has so-called Schema Previews where you can use new schema features - but it requires a custom Accept header.

I've used the Apollo client before but I'd like to try this new app with Formidables urlq. Is there a way to set customer headers with the urql client?

Update

I think this has gone into the codebase, it's just not documented - https://github.com/FormidableLabs/urql/pull/96/files

timbo
  • 13,244
  • 8
  • 51
  • 71

3 Answers3

8

Looking through the source, the following urql createClient works for me:

const client = createClient({
  url: 'https://api.github.com/graphql',
  fetchOptions: {
    headers: {
      Authorization: `bearer ${GITHUB_TOKEN}`,
      Accept: 'application/vnd.github.packages-preview+json',
    },
  },
})

Update

There's actually a better way to do this than my original answer. createClient accepts a function for fetchOptions. So if the token is present, it'll be added in an Authorization header

const client = createClient({
  url: 'http://localhost:8000/graphql/',
  // add token to header if present
  fetchOptions: () => {
    const token = getToken()
    return token ? { headers: { Authorization: `Bearer ${token}`, Accept: 'application/vnd.github.packages-preview+json' }} : {}
  },
})
timbo
  • 13,244
  • 8
  • 51
  • 71
4

For asynchronous token setting, you can use the auth exchange

import { createClient, dedupExchange, cacheExchange, fetchExchange } from 'urql';
import { authExchange } from '@urql/exchange-auth';

const getAuth = async ({ authState, mutate }) => {
  if (!authState) {
    const token = await getToken();
    const refreshToken = await getRefreshToken();
    if (token && refreshToken) {
      return { token, refreshToken };
    }
    return null;
  }

  return null;
};

const addAuthToOperation = ({ authState, operation }) => {
  if (!authState || !authState.token) {
    return operation;
  }

  const fetchOptions =
    typeof operation.context.fetchOptions === 'function'
      ? operation.context.fetchOptions()
      : operation.context.fetchOptions || {};

  return makeOperation(operation.kind, operation, {
    ...operation.context,
    fetchOptions: {
      ...fetchOptions,
      headers: {
        ...fetchOptions.headers,
        Authorization: authState.token,
      },
    },
  });
};

const client = createClient({
  url: '/graphql',
  exchanges: [
    dedupExchange,
    cacheExchange,
    authExchange({
      getAuthToken,
      addAuthToOperation,
    }),
    fetchExchange,
  ],
});

Jason
  • 179
  • 2
  • 4
  • 6
    It's a lot of BS to get some headers. – Oliver Dixon Nov 08 '21 at 17:54
  • If you already have the tokens cached, it's a much simpler implementation. If you want to load it asynchronously, then it's more complicated. I used this implementation because we wanted firebase to manage refetching the token if it expires, and that's promise-based – Jason Dec 07 '21 at 02:20
1

Here is what I use and it works okay:

const client = createClient({
  url: 'yoururl',
  fetchOptions: {
    headers: {
      'content-type': 'application/json',
      'x-hasura-admin-secret':'********'
    },
  },
});
David Buck
  • 3,752
  • 35
  • 31
  • 35
Kaliguy17
  • 36
  • 1
  • For browsers, you want to use a token that can be updated frequently. You can use a token provider such as firebase auth or oauth. Admin secret is designed for servers since they change infrequently and are not shared with many users. Also, admin role gives the request admin access so your access rules doesn't work – Jason Oct 19 '21 at 09:10