Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# StableRoute frontend environment
NEXT_PUBLIC_STABLEROUTE_API_BASE=http://localhost:3001

# For common API connection troubleshooting, see docs/TROUBLESHOOTING.md

# Theme preference is stored in localStorage under the key defined in src/lib/theme.ts
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,8 @@ The client reads this value in [`src/lib/apiClient.ts`](src/lib/apiClient.ts). R

### Troubleshooting

For a focused guide covering misconfigured API bases, CORS failures, timeouts, and 401/403 responses, see [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md).

| Symptom | Fix |
| ------------------------------------ | ----------------------------------------------------------------------- |
| API calls fail with `ECONNREFUSED` | Start the StableRoute backend or set `NEXT_PUBLIC_STABLEROUTE_API_BASE` |
Expand Down
31 changes: 31 additions & 0 deletions docs/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# API connection troubleshooting

When the frontend cannot reach the StableRoute API, the failure usually comes from one of four places: the configured backend origin, browser access restrictions, a slow or paused backend, or invalid credentials.

## Quick checks

1. Confirm the backend is running and reachable from the machine where the frontend is served.
2. Verify that NEXT_PUBLIC_STABLEROUTE_API_BASE points at the backend origin, not the frontend origin.
3. If the browser reports a blocked request, check the backend CORS configuration for the exact frontend host.
4. If the API responds with 401 or 403, confirm the API key is active and authorized for the route being used.

## Common symptoms and remedies

| Symptom | Where it originates | What to try |
| --- | --- | --- |
| Wrong or missing API base URL | src/lib/config.ts | Update NEXT_PUBLIC_STABLEROUTE_API_BASE to the backend origin such as https://api.example.com and restart the dev server. |
| Browser blocks the request with CORS errors | src/lib/apiClient.ts | Verify the backend allows the current frontend origin and that the configured base URL matches the backend host. |
| The request times out or feels slow | src/lib/apiClient.ts | Check the backend health, network path, and router status. If the backend is under load, retry with a higher timeout. |
| The API returns 401 or 403 | src/lib/apiClient.ts | Confirm the API key is valid, active, and permitted for the route or operation being called. |

## Useful commands

```bash
# Check the configured backend base
printf '%s\n' "$NEXT_PUBLIC_STABLEROUTE_API_BASE"

# Start the frontend after changing environment variables
npm run dev
```

For the full local setup and environment variable examples, see the main [README.md](../README.md) guide.
38 changes: 37 additions & 1 deletion src/lib/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { DEFAULT_API_BASE, getApiBase, validateApiBase } from '../config';
import {
DEFAULT_API_BASE,
getApiBase,
getApiConnectionTroubleshootingGuide,
validateApiBase,
} from '../config';

describe('config', () => {
const original = process.env.NEXT_PUBLIC_STABLEROUTE_API_BASE;
Expand All @@ -22,4 +27,35 @@ describe('config', () => {
it('rejects non-http(s) API base values', () => {
expect(() => validateApiBase('ftp://bad.example')).toThrow(/http/);
});

it('returns troubleshooting guidance for common API connection failures', () => {
const guide = getApiConnectionTroubleshootingGuide();

expect(guide).toHaveLength(4);
expect(guide).toEqual(
expect.arrayContaining([
expect.objectContaining({
symptom: 'Misconfigured base URL',
module: 'src/lib/config.ts',
action: 'Update NEXT_PUBLIC_STABLEROUTE_API_BASE to the backend origin.',
}),
expect.objectContaining({
symptom: 'CORS or browser-blocked request',
module: 'src/lib/apiClient.ts',
action:
'Verify the backend allows your frontend origin and that the API base points to the correct host.',
}),
expect.objectContaining({
symptom: 'Timeout or slow backend',
module: 'src/lib/apiClient.ts',
action: 'Check the backend health, network path, and retry with a higher timeout if needed.',
}),
expect.objectContaining({
symptom: '401 or 403 authorization failure',
module: 'src/lib/apiClient.ts',
action: 'Confirm the API key is valid, active, and authorized for the requested route.',
}),
])
);
});
});
33 changes: 33 additions & 0 deletions src/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
export type ApiConnectionTroubleshootingEntry = {
symptom: string;
module: string;
action: string;
};

/** Default local API base when no env override is set. */
export const DEFAULT_API_BASE = 'http://localhost:3001';

Expand All @@ -23,3 +29,30 @@ export function validateApiBase(value: string): void {
throw new Error('API base must use http or https');
}
}

/** Returns a compact guide for common API connection failures. */
export function getApiConnectionTroubleshootingGuide(): ApiConnectionTroubleshootingEntry[] {
return [
{
symptom: 'Misconfigured base URL',
module: 'src/lib/config.ts',
action: 'Update NEXT_PUBLIC_STABLEROUTE_API_BASE to the backend origin.',
},
{
symptom: 'CORS or browser-blocked request',
module: 'src/lib/apiClient.ts',
action:
'Verify the backend allows your frontend origin and that the API base points to the correct host.',
},
{
symptom: 'Timeout or slow backend',
module: 'src/lib/apiClient.ts',
action: 'Check the backend health, network path, and retry with a higher timeout if needed.',
},
{
symptom: '401 or 403 authorization failure',
module: 'src/lib/apiClient.ts',
action: 'Confirm the API key is valid, active, and authorized for the requested route.',
},
];
}