The Axios instance every authenticated request goes through: environment-aware base URL, a CSRF token attached to every mutation, and Authorization state kept in sync with localStorage.
// src/services/api/privateApi.ts
// lines 60–122 (instance + CSRF interceptor)
const API_BASE = resolveApiBase();
const privateApi: AxiosInstance = axios.create({
baseURL: API_BASE,
withCredentials: true,
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest",
},
});
type CommonAuthHeaders = {
Authorization?: string;
[key: string]: unknown;
};
type PrivateAxiosHeaders = AxiosHeaders & {
common?: CommonAuthHeaders;
};
function getMutableDefaultHeaders(): PrivateAxiosHeaders {
return privateApi.defaults.headers as unknown as PrivateAxiosHeaders;
}
// Bootstrap: hydrate Authorization from localStorage on page load
try {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("authToken");
if (stored) {
const headers = getMutableDefaultHeaders();
headers.common = { ...(headers.common ?? {}), Authorization: stored };
}
}
} catch {
void 0;
}
// Request: attach CSRF token on mutations
privateApi.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const method = (config.method ?? "get").toLowerCase();
if (["post", "put", "patch", "delete"].includes(method)) {
const m = document.cookie.match(/(?:^|;\s*)CSRF-TOKEN=([^;]+)/);
if (m) {
const token = decodeURIComponent(m[1]);
const headers = (config.headers ??= new AxiosHeaders(config.headers));
headers.set("X-CSRF-Token", token);
}
}
return config;
});
Base URL resolution is environment-aware (dev hits the local SSL Puma server directly); the CSRF token is read straight off the cookie set at login.
// src/services/api/privateApi.ts
// lines 60–122 (instance + CSRF interceptor)
const API_BASE = resolveApiBase();
const privateApi: AxiosInstance = axios.create({
baseURL: API_BASE,
withCredentials: true,
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest",
},
});
type CommonAuthHeaders = {
Authorization?: string;
[key: string]: unknown;
};
type PrivateAxiosHeaders = AxiosHeaders & {
common?: CommonAuthHeaders;
};
function getMutableDefaultHeaders(): PrivateAxiosHeaders {
return privateApi.defaults.headers as unknown as PrivateAxiosHeaders;
}
// Bootstrap: hydrate Authorization from localStorage on page load
try {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("authToken");
if (stored) {
const headers = getMutableDefaultHeaders();
headers.common = { ...(headers.common ?? {}), Authorization: stored };
}
}
} catch {
void 0;
}
// Request: attach CSRF token on mutations
privateApi.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const method = (config.method ?? "get").toLowerCase();
if (["post", "put", "patch", "delete"].includes(method)) {
const m = document.cookie.match(/(?:^|;\s*)CSRF-TOKEN=([^;]+)/);
if (m) {
const token = decodeURIComponent(m[1]);
const headers = (config.headers ??= new AxiosHeaders(config.headers));
headers.set("X-CSRF-Token", token);
}
}
return config;
});
// src/services/api/privateApi.ts
// lines 60–122 (instance + CSRF interceptor)
const API_BASE = resolveApiBase();
const privateApi: AxiosInstance = axios.create({
baseURL: API_BASE,
withCredentials: true,
headers: {
Accept: "application/json",
"X-Requested-With": "XMLHttpRequest",
},
});
type CommonAuthHeaders = {
Authorization?: string;
[key: string]: unknown;
};
type PrivateAxiosHeaders = AxiosHeaders & {
common?: CommonAuthHeaders;
};
function getMutableDefaultHeaders(): PrivateAxiosHeaders {
return privateApi.defaults.headers as unknown as PrivateAxiosHeaders;
}
// Bootstrap: hydrate Authorization from localStorage on page load
try {
if (typeof window !== "undefined") {
const stored = localStorage.getItem("authToken");
if (stored) {
const headers = getMutableDefaultHeaders();
headers.common = { ...(headers.common ?? {}), Authorization: stored };
}
}
} catch {
void 0;
}
// Request: attach CSRF token on mutations
privateApi.interceptors.request.use((config: InternalAxiosRequestConfig) => {
const method = (config.method ?? "get").toLowerCase();
if (["post", "put", "patch", "delete"].includes(method)) {
const m = document.cookie.match(/(?:^|;\s*)CSRF-TOKEN=([^;]+)/);
if (m) {
const token = decodeURIComponent(m[1]);
const headers = (config.headers ??= new AxiosHeaders(config.headers));
headers.set("X-CSRF-Token", token);
}
}
return config;
});