Search code examples
typescriptvue.jshoisting

"ReferenceError: can't access lexical declaration 'BaseRepository' before initialization" on page refresh


I have a BaseRepository abstract class in my code which contains client instance and some general functions. The AuthRepository class inherits from BaseRepository. And I have an AuthService module which uses AuthRepository to login and/or check login status. When I open a route I send a request to my API. I use AuthService to check and get token for authorization in client request interceptor. If I can get token for request I add it to headers and continue with request. If I cannot I redirect to login page to login again. It works fine when I first login from my login page and continue using web app but I get the error "ReferenceError: can't access lexical declaration 'BaseRepository' before initialization" if I refresh the page.

BaseRepository

import { BackendClient } from "@/repositories/clients";
import type { AxiosError, AxiosInstance, AxiosResponse } from "axios";

// other imports

export default abstract class BaseRepository {
    protected readonly client: AxiosInstance = BackendClient;
    // general functions
}

AuthRepository

import { BaseRepository } from "@/repositories/base";

class AuthRepository extends BaseRepository {
    constructor() {
        super()
    }
    // other functions
}

const authRepository = new AuthRepository();

export default authRepository;

Axios client

import axios, { type AxiosInstance } from "axios";

import { AuthService } from "@/modules/auth";
import router from "@/router";

import appConfig from "@/config";

const client: AxiosInstance = axios.create({
  baseURL: appConfig.backendClient.baseUrl,
  headers: {
    "Access-Control-Allow-Origin": "*",
  },
  validateStatus: (status: number): boolean => {
    var isValid = (status < 400 || status == 401);
    console.log("requestvalidatestatus", isValid);
    return isValid;
  }
})

const TOKEN_EXCLUDE_ENDPOINTS = [
  'Auth/Login',
  'Auth/Register',
  'Auth/RefreshAccessToken'
]

client.interceptors.request.use(async (config) => {
  var requestUrl = config.url;
  for(var excludeEndpoint of TOKEN_EXCLUDE_ENDPOINTS) {
    if(requestUrl?.startsWith(excludeEndpoint)){
      return config;
    }
  }

  var token = await AuthService.getAccessToken();

  if(!token) {
    router.push('/login');
  }
  console.log("token",token);
  config.headers.Authorization = `Bearer ${token}`;
  return config;
})

export default client;

AuthService

import { AuthRepository } from "@/repositories";

class AuthService {
    async login(request: LoginRequest, rememberMe: boolean, forceLogin: boolean = false) {
        // other logic
        return await AuthRepository.login(request);
    }
    // other functions
}

const authService = new AuthService();

export default authService;

What might be the problem here?


Solution

  • It was a circular dependency issue. I solved it by implementing request interceptor in main.ts.

    main.ts

    import { BackendClient } from './repositories/clients'
    import { AuthService } from './modules/auth'
    
    const TOKEN_EXCLUDE_ENDPOINTS = [
        'Auth/Login',
        'Auth/Register',
        'Auth/RefreshAccessToken'
    ]
    
    BackendClient.interceptors.request.use(async (config) => {
        var requestUrl = config.url;
        for (var excludeEndpoint of TOKEN_EXCLUDE_ENDPOINTS) { // Token istemeyen endpoint ise devam et
            if (requestUrl?.startsWith(excludeEndpoint)) {
                return config;
            }
        }
    
        var token = await AuthService.getAccessToken();
    
        if (!token) {
            router.push('/login');
        }
        console.log("token", token);
        config.headers.Authorization = `Bearer ${token}`;
        return config;
    })
    

    BackendClient.ts

    import axios, { type AxiosInstance } from "axios";
    
    import appConfig from "@/config";
    
    const client: AxiosInstance = axios.create({
      baseURL: appConfig.backendClient.baseUrl,
      headers: {
        "Access-Control-Allow-Origin": "*",
      },
      validateStatus: (status: number): boolean => {
        var isValid = (status < 400 || status == 401);
        console.log("requestvalidatestatus", isValid);
        return isValid;
      }
    })
    
    export default client;