Owner: Engineering Team | Last Updated: 2026-01-30 | Status: Current
The WWAI web app communicates with the Django backend via REST API calls. The codebase uses two different HTTP clients: axios (via a configured instance) and native fetch(). This is an intentional inconsistency — the humanizer uses fetch() while all other features use axios. For the full web app overview, see Web App Overview.
From utils/api.ts:
import axios from 'axios'
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_BACKEND_URL,
headers: {
'X-Walter-Internal-Key': process.env.WALTER_INTERNAL_KEY,
'User-Agent': 'walter-nextjs-app',
},
})
export default api
Key points:
NEXT_PUBLIC_BACKEND_URL environment variableX-Walter-Internal-Key header is included on all requests (server-side only — this env var is NOT prefixed with NEXT_PUBLIC_)User-Agent is hardcoded to walter-nextjs-appUsed by: Detector, settings persistence, file upload, auth, user management
import { detector } from '@/app/api/auth/register'
// Wrapper function from app/api/auth/register.ts
export const detector = async ({ data, token }) => {
const response = await axios({
url: `${process.env.NEXT_PUBLIC_BACKEND_URL}/api/feature/detector/`,
method: 'post',
data: data,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
})
return response
}
Used by: HumanizeButton.tsx
const response = await fetch(
`${process.env.NEXT_PUBLIC_BACKEND_URL}/api/feature/humanizer/`,
{
method: 'POST',
body: JSON.stringify(userData),
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${sanitizedToken}`,
},
}
)
The humanizer likely uses fetch() for streaming support or historical reasons. Both patterns use the same auth header format.
All authenticated requests include:
Authorization: Bearer <session.access_token>
Content-Type: application/json
For auth-related requests (login, OAuth), additional IP headers are set:
const clientIp = await getClientIp()
if (clientIp) {
requestHeaders['X-Forwarded-For'] = clientIp
requestHeaders['X-Client-IP'] = clientIp
}
From app/api/auth/register.ts:
| Function | Endpoint | HTTP Client | Purpose |
|---|---|---|---|
signUp |
POST /api/user/signup/ |
axios | User registration |
humanize |
POST /api/feature/humanizer/ |
axios | Humanize text (wrapper — but HumanizeButton uses fetch) |
detector |
POST /api/feature/detector/ |
axios | AI detection scan |
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/api/user/login/ |
POST | No | Email/password login |
/api/user/login/google/ |
POST | No | Google OAuth login |
/api/user/login/facebook/ |
POST | No | Facebook OAuth login |
/api/user/signup/ |
POST | No | User registration |
/api/user/refresh/ |
POST | No | Refresh JWT tokens |
/api/user/account/ |
GET | Bearer | Get user profile/account data |
/api/feature/humanizer/ |
POST | Bearer | Humanize text |
/api/feature/free-humanizer/ |
POST | Bearer | Free tier humanization |
/api/feature/detector/ |
POST | Bearer | AI detection scan |
/api/feature/ai-detector/ |
POST | Bearer | Alternative AI detection |
/api/feature/file-to-html/ |
POST | Bearer | Convert uploaded file to text |
/api/feature/remember-humanizer-selection/ |
GET/POST | Bearer | Load/save humanizer preferences |
There are no global error interceptors. Each component handles errors individually. The common pattern:
if (axios.isAxiosError(err)) {
const status = err.response?.status
const detail = err.response?.data?.detail
const message = err.response?.data?.message
|| err.response?.data?.error
|| err.response?.data?.detail
|| fallbackMessage
}
if (!response.ok) {
const errorData = await response.json().catch(() => ({}))
const errorMessage = errorData.detail
|| errorData.message
|| errorData.error
|| fallbackMessage
}
| Status | Meaning | Frontend Handling |
|---|---|---|
| 401 | Token expired / invalid | Redirect to login |
| 403 | Subscription paused, plan limit, or insufficient credits | Contextual UI |
| 429 | Rate limited | Toast error |
| 500 | Server error | Generic error toast |
The api instance from utils/api.ts is used server-side in server/auth.ts for:
api.post('/api/user/login/', credentials)api.post('/api/user/login/google/', userData)api.post('/api/user/refresh/', refreshBody)api.get('/api/user/account/', { headers: { Authorization } })These server-side calls include the X-Walter-Internal-Key header automatically.
NEXT_PUBLIC_BACKEND_URL| Date | Author | Change |
|---|---|---|
| 2026-01-30 | Admin | Initial creation |
| 2026-01-30 | Docs team | Rewritten with real code: actual axios config, dual HTTP client pattern (axios + fetch), endpoint table, error handling patterns |
Prev: Web App - Component Library | Next: Web App - Environment Variables | Up: WalterWrites