Table of Contents#
- Understanding the Problem: Why Cancel Requests Without Tokens?
- Axios Interceptors: A Quick Refresher
- Cancel Tokens in Axios: Old vs. New Approaches
- Step-by-Step Solution: Canceling Requests in Interceptors
- Fixing the 'cancelToken' Undefined Error
- Testing Your Implementation
- Best Practices for Request Cancellation
- Conclusion
- References
Understanding the Problem: Why Cancel Requests Without Tokens?#
Before diving into the solution, let’s clarify why canceling requests without tokens matters:
- Security: Prevents sensitive endpoints from receiving unauthenticated requests.
- Performance: Reduces unnecessary server load and network traffic.
- User Experience: Avoids misleading 401/403 errors that may confuse users (e.g., "Login required" is clearer than a generic error).
- Clean Code: Centralizes token validation logic in interceptors, avoiding redundant checks in every request.
The most common pitfall here is the 'cancelToken' undefined error. This occurs when Axios tries to use a cancel token that hasn’t been properly initialized, often due to incorrect usage of Axios’ cancellation APIs.
Axios Interceptors: A Quick Refresher#
Axios interceptors let you "intercept" requests or responses before they are handled by then/catch. For our use case, request interceptors are ideal: they run before a request is sent, allowing us to check for a token and cancel the request if needed.
A basic request interceptor looks like this:
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com'
});
// Request interceptor
api.interceptors.request.use(
(config) => {
// Modify config before request is sent (e.g., add headers, check tokens)
return config;
},
(error) => {
// Handle request error
return Promise.reject(error);
}
);We’ll use this interceptor to check for a valid token. If none exists, we’ll cancel the request here.
Cancel Tokens in Axios: Old vs. New Approaches#
Axios supports two methods for canceling requests:
1. CancelToken (Legacy, Deprecated)#
Historically, Axios used CancelToken (inspired by Bluebird’s cancellation API). However, CancelToken is deprecated in Axios v0.22.0+ in favor of the standard AbortController (see below).
Example with CancelToken:
import axios, { CancelToken } from 'axios';
const source = CancelToken.source();
api.get('/data', {
cancelToken: source.token
});
// Cancel the request later
source.cancel('Request canceled due to missing token');2. AbortController (Recommended)#
The modern, standardized approach is to use the Web API AbortController, which is supported by most browsers and aligns with fetch API conventions.
Example with AbortController:
const controller = new AbortController();
api.get('/data', {
signal: controller.signal
});
// Cancel the request later
controller.abort('Request canceled due to missing token');Since CancelToken is deprecated, we’ll focus on AbortController for the solution. However, we’ll also address the 'cancelToken' undefined error for legacy implementations.
Step-by-Step Solution: Canceling Requests in Interceptors#
Let’s walk through implementing request cancellation when no token exists, using AbortController (recommended) and fixing common errors.
Step 1: Define a Token Retrieval Function#
First, create a helper to fetch the token from storage (e.g., localStorage, sessionStorage, or cookies). Adjust this based on where your app stores tokens:
// utils/auth.js
export const getAuthToken = () => {
// Example: Retrieve token from localStorage
return localStorage.getItem('authToken');
};Step 2: Create an Axios Instance with Interceptor#
Create a dedicated Axios instance (to avoid polluting global Axios) and add a request interceptor. Inside the interceptor:
- Check if a token exists using
getAuthToken(). - If no token, cancel the request with
AbortController. - If a token exists, add it to the request headers (optional but common).
Full Implementation with AbortController:#
// api/client.js
import axios from 'axios';
import { getAuthToken } from '../utils/auth';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000
});
// Request interceptor to check for token and cancel if missing
api.interceptors.request.use(
(config) => {
const token = getAuthToken();
if (!token) {
// No token: Cancel the request using AbortController
const controller = new AbortController();
// Attach the abort signal to the request config
config.signal = controller.signal;
// Abort immediately with a reason
controller.abort('Request canceled: No authentication token found');
} else {
// Token exists: Add it to headers (optional)
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
export default api;How It Works#
- No Token: We create an
AbortController, attach itssignalto the request config, and immediately callcontroller.abort(). This cancels the request before it’s sent. - With Token: The request proceeds with the token in the
Authorizationheader.
Handling Cancelled Requests in Components#
When a request is aborted, Axios rejects the promise with an AbortError. Catch this in your components to handle cancellation gracefully (e.g., redirect to login):
// components/DataFetcher.js
import api from '../api/client';
const fetchData = async () => {
try {
const response = await api.get('/protected-data');
return response.data;
} catch (error) {
if (error.name === 'AbortError') {
// Handle cancellation (e.g., redirect to login)
console.log('Request cancelled:', error.message);
window.location.href = '/login';
} else {
// Handle other errors (e.g., network issues, 401)
console.error('Request failed:', error);
}
}
};Fixing the 'cancelToken' Undefined Error#
The 'cancelToken' undefined error typically occurs in two scenarios:
Scenario 1: Using Deprecated CancelToken Without Initialization#
If you’re using the legacy CancelToken approach and forget to initialize the cancel source, source.token will be undefined.
Common Mistake:
// ❌ Incorrect: source is not initialized
api.interceptors.request.use((config) => {
const token = getAuthToken();
if (!token) {
config.cancelToken = source.token; // source is undefined!
source.cancel('No token');
}
return config;
});Fix for CancelToken:
Always initialize CancelToken.source() before using it:
// ✅ Correct: Initialize source first
import { CancelToken } from 'axios';
api.interceptors.request.use((config) => {
const token = getAuthToken();
if (!token) {
const source = CancelToken.source(); // Initialize source
config.cancelToken = source.token; // Now source.token is defined
source.cancel('Request canceled: No token');
}
return config;
});Scenario 2: Mixing cancelToken and signal#
If you’re using AbortController (which uses signal) but accidentally reference cancelToken in the config, Axios will throw 'cancelToken' undefined if cancelToken isn’t set.
Common Mistake:
// ❌ Incorrect: Using cancelToken with AbortController
const controller = new AbortController();
config.cancelToken = controller.signal; // Should be config.signal!Fix for AbortController:
Use signal (not cancelToken) with AbortController:
// ✅ Correct: Use config.signal
const controller = new AbortController();
config.signal = controller.signal; // Attach signal to config
controller.abort('No token');Testing the Implementation#
To verify your setup:
- Clear the Token: Delete the token from
localStorage(or your storage method) to simulate a logged-out user. - Trigger a Request: Call an API endpoint using your
apiinstance (e.g.,fetchData()). - Check DevTools: In Chrome DevTools > Network tab, the request should show as "cancelled" (status: "(failed) net::ERR_ABORTED").
- Catch the Error: Ensure your component’s
catchblock logs theAbortErrorand redirects to login.
Best Practices#
-
Use
AbortControllerOverCancelToken:
CancelTokenis deprecated, so adoptAbortControllerfor future-proof code. -
Centralize Token Logic:
Keep token retrieval (e.g.,getAuthToken()) in a utility function to avoid duplication. -
Handle Token Expiry Separately:
This solution cancels requests when no token exists. For expired tokens, use a response interceptor to refresh the token instead of canceling. -
Avoid Silent Failures:
Always catchAbortErrorin components to inform users (e.g., "Please log in to continue"). -
Clean Up Abort Controllers:
For long-running requests (e.g., polling), store controllers in a list and abort them when components unmount to prevent memory leaks.
Conclusion#
Canceling Axios requests in interceptors when no token exists is critical for security and performance. By using AbortController (Axios’ recommended approach) and properly initializing cancellation signals, you can avoid the 'cancelToken' undefined error.
Key takeaways:
- Use
AbortControllerfor modern, standardized cancellation. - Always initialize cancellation sources/signals before using them.
- Handle
AbortErrorin components to guide users (e.g., redirect to login).
With this setup, your app will gracefully handle missing tokens and avoid unnecessary server requests.