Request System
A framework wrapper around Nuxt's built-in $fetch, providing AES-encrypted transport, automatic token injection and renewal, unified response format unwrapping (status/data/error/errorCode), and error interception.
Basic Usage
The framework exposes the request() function via global auto-import:
// Basic request (POST by default)
const data = await request('/api/user/info')
// Request with a body (auto-serialized)
const result = await request('/api/login', {
body: { username: 'admin', password: '123456' },
})
// With query parameters
const list = await request('/api/products', {
params: { page: 1, pageSize: 20, keyword: 'search term' },
})
Public plain JSON metadata exception
When an OpenAPI operation explicitly declares x-sw-protocol: plain-json, generate-api skips it instead of generating an encrypted request() client wrapper. Call these metadata GETs using native server $fetch according to the backend contract, with a fixed tenant identity and no forwarded visitor cookies, Authorization, or signing parameters. x-sw-visibility: public describes access eligibility and does not replace the transport marker. Existing unmarked business operations retain the encrypted protocol; an unknown x-sw-protocol fails generation.
These new APIs describe local changes awaiting release; they have not been published. Verify the actual framework version before adoption.
Response Format Convention
The framework expects a unified backend response format (after the response body is decrypted); request() automatically unwraps the data field:
// Backend returns (decrypted)
{ "status": "y", "data": { "id": 1, "name": "John" } }
// request() returns just the data portion
const user = await request('/api/user/info')
// user = { id: 1, name: "John" }
When status === 'n', request() throws an Error (its message is the backend error field) and does not show a toast automatically — the caller is expected to try/catch and surface feedback to the user.
Plaintext failure envelopes: the legacy backend is a mixed protocol — most responses are encrypted, but some business failures (e.g. the login endpoint's "wrong account or password" 4002) come back as plaintext JSON with the same Content-Type as encrypted responses. request() detects such plaintext failure envelopes before decrypting and throws the same ApiError, so business code does not need to tell them apart. Only plaintext status: 'n' is accepted; plaintext status: 'y' or arbitrary JSON is treated as a protocol error. Decryption failures (key / transport mismatch, a non-protocol body from the backend) are normalised into an Error whose message starts with [swift-web] 响应报文无法解密, instead of leaking low-level exceptions such as Invalid typed array length.
HTTP 200 with an empty body
In rare cases the backend returns HTTP 200 with an empty body. request() then resolves to undefined (even though the signature still says Promise<T>) and does not throw — that is the established behaviour of the legacy protocol, which the legacy frontend passed through untouched as well.
The catch is that consumers receiving undefined render an empty state that is indistinguishable from "the backend genuinely has no data", leaving nothing to go on when something looks wrong. The framework therefore emits a development-only warning for it (once per endpoint; the code is stripped from production builds):
[swift-web] POST /movie/home 返回 HTTP 200 但 body 为空 → request() 返回 undefined。
When you see this warning, first confirm whether that endpoint is allowed to return nothing. If it is not, the problem is on the backend — don't paper over it with a frontend fallback.
Automatic Token Injection
request() carries identity credentials automatically: the request body is wrapped as { data, token, deviceId } (token read from useUserStore().token), gzipped and AES-CBC encrypted, then sent as text/plain — no need to pass headers manually.
Sending as text/plain above is the default base64 transport encoding; for a backend using a raw-binary encrypted protocol, switch to request.transport: 'binary' (raw byte stream both ways) — see Module Options · request.
Configuration
Environment-specific values live in .env (loaded by nuxi at build time and baked into the artifact):
# .env — values that differ per environment
NUXT_PUBLIC_API_BASE=https://api.example.com # backend API base URL
apiPrefix / apiVersion / apiKey are project constants (identical across environments) and are written as real values in runtimeConfig.public of nuxt.config.ts, shipping with the code; a same-named NUXT_PUBLIC_* process env can still override them at runtime (native Nuxt mechanism).
Error Handling
try {
const data = await request('/api/user/sensitive-info')
} catch (e) {
// request() throws an Error on failure (message = backend error field or network error)
// Token expiry (errorCode 2002) is silently re-authenticated and retried internally
showToast(e instanceof Error ? e.message : 'Request failed, please retry')
}
useFetch vs request()
| Scenario | Recommended approach |
|---|---|
| Page initial data (SSR) | useFetch() or useAsyncData() |
| User-action-triggered requests | request() (no reactive wrapper, more concise) |
| Need cancellation / re-fetch | useFetch() (built-in abort and refresh) |
| Server-side API routes | $fetch() (Nitro built-in, no token needed) |
Token-expiry auto-refresh
request() intercepts errorCode === 2002 (expired token) during response unwrapping. It calls silentLogin() to re-establish identity, then retries the original request once with _retry=true — zero awareness on the business side. If the retry still fails, it throws Login expired, please refresh for upstream handling.
Note: this mechanism is client-only. On the server side, a 2002 response is treated as unauthenticated (no retry), because the server cannot re-establish a device identity (a re-login would write Set-Cookie into the response header, which an ISR cache would broadcast).
Further reading
- Auto-Login + Token Refresh Flow — full sequence diagram (including the 2002 intercept-retry path) + behavior across rendering modes
- useAuthReady — the client-side gate inside
request() - Rendering Quick Guide — anti-patterns when choosing ISR / SSR / CSR