Setting authorization header from local storage race issue?

import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';

const httpLink = createHttpLink({
  uri: '/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('token');
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    }
  }
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

In this line,
const token = localStorage.getItem('token');
we mostly use async/await to get data from local storage, in that case authLink will be async function right?

i believe that means,

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

this lines run before we fulfill async httpLink function call, is not this a race issue? will apollo assign httpLink to authLink.concat after promise is fulfilled ??

1 Like

Hi @rosnk, did you ever figure this out? I am experiencing the same issue.

Hi, did you get it to work? I am also experiencing the same issue.

Thanks for posting, all - I’d be happy to help out. @golsah can you share some sample code so the community can see what you see?

@rosnk the Apollo Link docs might be able to answer your questions about how the link chain functions. To my knowledge, Storage#getItem is a synchronous operation. The return value of setContext is an instance of ApolloLink, which is not an async function.

I hope that helps move the conversation forward, thanks again!

Sure! I am creating a React SignUp form based on my GraphQL API. The API perfectly works but the React App doesn’t. There is “Unexpected token” error in my signup.js file, which can be seen below.

And the error is:

44 | const onChange = event => {
45 | setValues({

46 | …values,
| ^
47 | [event.target.name]: event.target.value
48 | });
49 | };

import React, { useEffect, useState } from 'react';
import styled from 'styled-components';
import { useMutation, useApolloClient, gql } from '@apollo/client';
import { useNavigate } from 'react-router-dom';

import Button from '../components/Button';

const Wrapper = styled.div`
  border: 1px solid #f5f4f0;
  max-width: 500px;
  padding: 1em;
  margin: 0 auto;
`;

const Form = styled.form`
  label,
  input {
    display: block;
    line-height: 2em;
  }

  input {
    width: 100%;
    margin-bottom: 1em;
  }
`;

const SIGNUP_USER = gql`
mutation signUp($email: String!, $username: String!, $password: String!) {
  signUp(email: $email, username: $username, password: $password)
}
`;

const SignUp = () => {

  const [ values, setValues ] = useState({});

  const navigate = useNavigate();

  useEffect(() => {
    document.title = 'Sign Up — Notedly';
  });

  const onChange = event => {
    setValues({
      ...values,
      [event.target.name]: event.target.value
    });
  };

  const [signUp, { loading, error }] = useMutation(SIGNUP_USER, {
    onCompleted: data => {
      localStorage.setItem('token', data.signUp);
      navigate('/');
    }
  });

  return (
    <Wrapper>
      <h2>Sign Up</h2>
      <Form onSubmit={event => {
          event.preventDefault();
          signUp({
            variables: {
              ...values
            }
          });
        }}>
        <label htmlFor="username">Username:</label>
        <input
          required
          type="text"
          id="username"
          name="username"
          placeholder="username"
          onChange={onChange}
        />
        <label htmlFor="email">Email:</label>
        <input
          required
          type="email"
          id="email"
          name="email"
          placeholder="Email"
          onChange={onChange}
        />
        <label htmlFor="password">Password:</label>
        <input
          required
          type="password"
          id="password"
          name="password"
          placeholder="Password"
          onChange={onChange}
        />
        <Button type="submit">Submit</Button>
      </Form>
    </Wrapper>
  );
};

export default SignUp;