AJAX - AJAX Request Interceptors and Response Interceptors
AJAX request and response interceptors are mechanisms used to process HTTP requests before they are sent to the server and to process responses after they are received from the server. They provide a centralized way to modify, inspect, or handle requests and responses instead of writing the same logic separately for every AJAX call. Interceptors are particularly useful in large web applications where many components communicate with APIs.
1. What Is an AJAX Request Interceptor?
A request interceptor runs before an AJAX request is sent to the server. It can inspect or modify the request according to the application's requirements.
For example, an application may need to add an authentication token to every API request. Without an interceptor, developers would have to manually add the token to every AJAX call. With a request interceptor, the token can be added automatically.
A request interceptor can be used to:
-
Add authentication or authorization information.
-
Add common HTTP headers.
-
Add request identifiers.
-
Modify request parameters.
-
Add timestamps.
-
Log outgoing requests.
-
Validate request data.
-
Display a loading indicator.
-
Apply common configuration settings.
The main advantage is centralization. Common request-related operations can be handled in one place.
2. Basic Request Interceptor Flow
The general process is:
Application
|
v
AJAX Request
|
v
Request Interceptor
|
|-- Add headers
|-- Add authentication
|-- Modify parameters
|-- Log request
|
v
Server
For example, suppose an application sends the following request:
fetch("/api/users");
A request interceptor could conceptually transform it into:
fetch("/api/users", {
headers: {
"Authorization": "Bearer TOKEN"
}
});
The application code does not need to manually add the authorization header every time.
3. What Is an AJAX Response Interceptor?
A response interceptor runs after the server sends a response but before that response is processed by the application.
It can inspect the HTTP status, response headers, response data, or other information and then decide how the application should handle the response.
A response interceptor can be used to:
-
Check HTTP status codes.
-
Process common response formats.
-
Handle authentication failures.
-
Transform response data.
-
Log server responses.
-
Detect application-level errors.
-
Display common error messages.
-
Remove unnecessary response information.
-
Trigger actions based on specific responses.
The basic flow is:
Server
|
v
HTTP Response
|
v
Response Interceptor
|
|-- Check status
|-- Process errors
|-- Transform data
|-- Log response
|
v
Application
4. Why Are Interceptors Useful?
Consider an application containing 50 different AJAX requests.
Suppose every request needs:
Authorization header
Request logging
Common headers
Error handling
Response logging
Without interceptors, developers may have to repeat this logic in many places.
For example:
fetch("/api/users", {
headers: {
"Authorization": "Bearer TOKEN"
}
});
Another request may contain the same logic:
fetch("/api/products", {
headers: {
"Authorization": "Bearer TOKEN"
}
});
Another request may repeat it again.
This creates duplicated code and makes maintenance more difficult.
With an interceptor-based architecture, common processing can be centralized:
Request 1
|
Request
Interceptor
|
Server
Request 2
|
Request
Interceptor
|
Server
Request 3
|
Request
Interceptor
|
Server
The same centralized processing can be applied to every request.
5. Request Interceptor Example
Consider a simple interceptor function:
function requestInterceptor(url, options = {}) {
options.headers = {
...options.headers,
"X-Application": "MyWebApp"
};
console.log("Sending request:", url);
return fetch(url, options);
}
The application can use:
requestInterceptor("/api/users");
The interceptor adds the common header before the request reaches the server.
Another example is adding an authorization token:
function requestInterceptor(url, options = {}) {
const token = localStorage.getItem("token");
options.headers = {
...options.headers,
"Authorization": `Bearer ${token}`
};
return fetch(url, options);
}
Now the authorization information can be added centrally.
6. Response Interceptor Example
A simple response-processing function can be written as:
async function responseInterceptor(response) {
if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}
return response.json();
}
It can be used as:
fetch("/api/users")
.then(responseInterceptor)
.then(data => {
console.log(data);
})
.catch(error => {
console.error(error);
});
Here, the response interceptor checks whether the response was successful before converting the response into JSON.
7. Handling Common HTTP Errors
Response interceptors are useful when an application needs common handling for HTTP errors.
For example:
async function responseInterceptor(response) {
if (response.status === 401) {
console.log("Authentication required");
}
if (response.status === 403) {
console.log("Access denied");
}
if (response.status === 404) {
console.log("Resource not found");
}
if (response.status >= 500) {
console.log("Server error");
}
return response;
}
This allows the application to respond consistently to common server responses.
8. Request and Response Interceptors Together
Both types can be combined into a single AJAX communication pipeline:
Application
|
v
Request Interceptor
|
| Add headers
| Add token
| Log request
|
v
AJAX Request
|
v
Server
|
v
AJAX Response
|
v
Response Interceptor
|
| Check status
| Process errors
| Transform data
|
v
Application
This creates a predictable structure for communication between the browser and the server.
9. Interceptors and Authentication
One of the common uses of request interceptors is authentication.
Suppose an application stores an access token:
const token = localStorage.getItem("accessToken");
A request interceptor can attach it:
function addAuthentication(url, options = {}) {
const token = localStorage.getItem("accessToken");
options.headers = {
...options.headers,
Authorization: `Bearer ${token}`
};
return fetch(url, options);
}
Every request using this function can receive the required authentication information.
However, sensitive authentication tokens should be handled carefully. Storing credentials in browser-accessible storage can introduce security risks, especially in applications vulnerable to cross-site scripting.
10. Interceptors for Request Logging
Interceptors can also provide centralized logging.
function requestInterceptor(url, options = {}) {
console.log("Request URL:", url);
console.log("Request method:", options.method || "GET");
return fetch(url, options);
}
This can help developers understand:
-
Which endpoint was requested.
-
Which HTTP method was used.
-
When requests were generated.
-
Whether expected headers were included.
Logging is particularly useful during application development and debugging.
11. Response Data Transformation
An interceptor can transform data before passing it to the rest of the application.
Suppose an API returns:
{
"status": "success",
"data": [
{
"id": 1,
"name": "John"
}
]
}
The application may only need the data property.
A response interceptor could process the response:
async function responseInterceptor(response) {
const result = await response.json();
return result.data;
}
The rest of the application can then work directly with the user array instead of repeatedly accessing:
result.data
This keeps API-specific processing in one location.
12. Advantages of AJAX Interceptors
Centralized Processing
Common request and response operations can be maintained in one location.
Reduced Code Duplication
Developers do not need to repeat authentication, logging, header management, and common response processing in every AJAX call.
Consistent Error Handling
Different parts of an application can follow the same response-handling rules.
Easier Maintenance
If a common header or processing rule changes, developers can update the interceptor instead of modifying numerous AJAX requests.
Better Debugging
Interceptors can provide centralized request and response logging.
Separation of Responsibilities
Individual application components can concentrate on their specific tasks while common communication logic remains in the interceptor layer.
13. Limitations and Considerations
Interceptors should not be used for every piece of AJAX logic.
If an operation is specific to only one API request, putting it into a global interceptor can make the application harder to understand.
For example, a response interceptor that contains many endpoint-specific conditions can become complicated:
if (url === "/api/users") {
// user-specific processing
}
if (url === "/api/orders") {
// order-specific processing
}
if (url === "/api/products") {
// product-specific processing
}
When an interceptor becomes too large, maintaining it can become difficult.
Therefore, interceptors are most appropriate for common, cross-cutting operations that apply to many requests.
14. Interceptors vs Individual AJAX Handling
Without an interceptor:
Request 1 → Add headers → Send
Request 2 → Add headers → Send
Request 3 → Add headers → Send
Request 4 → Add headers → Send
With an interceptor:
Request 1 ─┐
Request 2 ─┤
Request 3 ─┼→ Request Interceptor → Server
Request 4 ─┘
Similarly, responses can pass through a common response-processing layer.
This makes the overall application architecture cleaner when the same processing rules apply to multiple AJAX operations.
15. Important Difference from AJAX Itself
AJAX is the communication technique that allows a web page to communicate with a server without necessarily reloading the entire page.
An interceptor is an additional software-design mechanism placed around that communication.
Therefore:
AJAX
= Communication between browser and server
Request Interceptor
= Processing before the request is sent
Response Interceptor
= Processing after the response is received
Interceptors do not replace AJAX. They organize and control how AJAX requests and responses are processed.
Conclusion
AJAX request and response interceptors provide a centralized processing layer around asynchronous HTTP communication. A request interceptor operates before a request reaches the server, allowing common headers, authentication information, logging, and other request-level processing to be applied consistently. A response interceptor operates after the server responds and can handle status codes, common errors, data transformation, and response logging.
Their primary value is in reducing repeated code and creating consistent communication behavior across a web application. They are particularly useful in large applications where many components communicate with the same APIs, but they should be kept focused on common operations rather than becoming a collection of endpoint-specific business logic.