跪拜 Guibai
← All articles
Frontend

Stop Hardcoding Your Axios Singleton: A Three-Layer Architecture for Maintainable Frontend Requests

By Liora_Yvonne ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

A request layer that directly imports the router, a UI component library, or hardcodes localStorage becomes impossible to reuse across Electron, mobile webviews, or different apps without rewriting it. The three-layer pattern with dependency injection keeps the services package portable and testable, which matters the moment a project adds a second backend or a second runtime target.

Summary

Most frontend projects start with a simple Axios instance that reads tokens from localStorage, redirects on 401, and shows error toasts. As backends multiply and requirements diverge, that single file accumulates conditionals for different success codes, auth schemes, and UI behaviors until no one dares to touch it. The root cause is coupling: the request layer knows too much about the application's router, component library, and storage mechanism.

The fix is a three-layer architecture. A request kernel (an AxiosFactory) creates instances and mounts interceptors without knowing any business logic. Service protocol layers handle per-backend differences like success codes and data field names. An application assembly layer injects runtime capabilities — token retrieval, auth failure handling, error display — at startup through functions like `configureServiceAuth` and `setErrorMessenger`. This inverts dependencies so the services layer never imports the router or a UI library.

Request-level behavior switches such as `hiddenNotify` and `skipAuthRedirect` prevent one-size-fits-all error handling from degrading the user experience during polling, silent token refresh, or optional data loading. The approach scales from single apps to monorepos, and the article provides concrete directory structures and a seven-question self-check for evaluating any request layer design.

Takeaways
Hardcoding token retrieval from localStorage and 401 redirects to a specific route couples the request layer to one application and one runtime environment.
A single global Axios instance forces all backend protocol differences into one file, creating a black hole of conditionals that nobody wants to modify.
An AxiosFactory that produces per-service instances lets each backend get its own interceptor while sharing the same HTTP kernel.
Token acquisition, authentication failure handling, and error display should be injected by the application at bootstrap time, not imported by the services layer.
Request-level options like `hiddenNotify` and `skipAuthRedirect` prevent polling and silent probe requests from triggering unwanted toasts or login redirects.
LYStack implements this pattern with `configureServiceAuth` for auth injection and `setErrorMessenger` for UI-independent error display.
A seven-question self-check — covering router imports, UI library imports, singleton count, interceptor conditionals, behavior switches, token hardcoding, and error layering — reveals whether a request layer is maintainable.
Simple projects with one backend and a short lifespan do not need this architecture; it serves complexity, not appearances.
Conclusions

The request layer's decay is not a failure of Axios but of boundary discipline: when a module knows about routers, UI components, and storage, it stops being a service and becomes an application hub.

Conditional branches keyed on `serviceType` inside a single interceptor are a reliable signal that the abstraction has collapsed and per-service instances are overdue.

Dependency inversion in the request layer — injecting `onAuthenticationFailure` rather than importing the router — is the same principle that keeps clean architecture testable, applied to a place frontend teams rarely apply it.

The `hiddenNotify` and `skipAuthRedirect` flags are not cosmetic; they acknowledge that HTTP requests in a real app have different UX contracts, and a uniform error handler violates those contracts.

LYStack's approach treats the services package as a capability provider that the app layer configures, which means the same services code can serve a web admin, an Electron shell, and a mobile webview without modification.

Concepts & terms
AxiosFactory
An instantiable factory that creates Axios instances with configurable interceptors, allowing multiple backend services to share the same HTTP kernel while maintaining separate protocol handling logic.
Dependency inversion in the request layer
Instead of the services layer importing application-level modules like the router or a UI library, the application injects functions (e.g., for token retrieval, auth failure handling, error display) into the services layer at startup.
Service protocol layer
A per-backend interceptor that handles protocol differences such as success codes, data field names, and authentication header formats, keeping those variations out of the shared request kernel.
hiddenNotify / skipAuthRedirect
Per-request configuration flags that suppress automatic error toasts or login redirects for requests that should fail silently, such as polling, token refresh probes, or optional data fetches.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗