Theme Library API Base URL Fix

Fixed critical issue where ThemeHttpStorageService was calling the frontend instead of the backend

Theme Library API Base URL Fix

Critical Bug Found

The Theme Library was showing "No global themes retrieved yet" because ThemeHttpStorageService was making requests to the frontend URL instead of the backend API!

The Problem

Error in Browser Console:

🚨 HTTP API Error getting all themes: HttpErrorResponse {
  status: 200,
  statusText: 'OK',
  url: 'http://localhost:5701/api/themes',  // ❌ WRONG - Frontend!
  ok: false
}

Expected URL:

http://localhost:3000/api/themes  // ✅ Correct - Backend!

Root Cause

ThemeHttpStorageService was using relative URLs (/api/themes) which Angular resolved relative to the current page origin (frontend port 5701), not the backend port (3000).

// ❌ BEFORE: Relative URL (resolves to frontend)
this.http.get('/api/themes')
// Results in: http://localhost:5701/api/themes (WRONG!)

The frontend doesn't have a /api/themes endpoint, so it returned the Angular app HTML (200 OK), which failed to parse as JSON, causing the error.

Why Other Services Worked

Other services like SpaceApiService were already injecting API_BASE_URL and using absolute URLs:

// ✅ SpaceApiService (working correctly)
export class SpaceApiService {
  private readonly baseUrl = inject(API_BASE_URL); // Gets 'http://localhost:3000/api'
  
  getCurrentSpace(): Observable<Space> {
    return this.http.get(`${this.baseUrl}/spaces/me`);
    // Results in: http://localhost:3000/api/spaces/me (CORRECT!)
  }
}

The Fix

File: libs/theme-system/theme-runtime/src/lib/api/theme-http-storage.service.ts

  1. Import API_BASE_URL token:
import { API_BASE_URL } from '@libs-foundational/core-angular';
  1. Inject the base URL:
export class ThemeHttpStorageService {
  private readonly http = inject(HttpClient);
  private readonly logger = inject(ThemeLoggerService);
  private readonly baseUrl = inject(API_BASE_URL); // ✅ NEW
}
  1. Use absolute URLs in all methods:
// ✅ AFTER: Absolute URL using injected baseUrl
getAllThemes(): Observable<Theme[]> {
  const url = `${this.baseUrl}/themes`; // http://localhost:3000/api/themes
  return this.http.get<{status: number; data: Theme[]}>(url).pipe(...)
}

Updated Methods

All HTTP methods were updated to use ${this.baseUrl}/themes:

Files Modified

  1. libs/theme-system/theme-runtime/src/lib/api/theme-http-storage.service.ts

    • Added API_BASE_URL injection
    • Updated all HTTP calls to use ${this.baseUrl}/themes
    • Fixed 9 methods total
  2. apis/backend-api/src/app/themes/themes.controller.ts (from previous fix)

    • Made GET endpoints public (no auth required)
  3. apis/backend-api/src/app/spaces/spaces.service.ts (from previous fix)

    • Fixed date format consistency
  4. apis/backend-api/src/app/themes/themes.service.ts (from previous fix)

    • Enhanced logging

Testing

1. Wait for Frontend Recompile

The frontend should automatically detect the changes and recompile. Watch Terminal 1 for:

Application bundle generation complete. [X.XXX seconds]
Component update sent to client(s).

2. Refresh Browser

3. Open Developer Console

Press F12 and check for these logs:

✅ Success Logs:

🎨 [DEBUG] ThemeHttpStorageService.getAllThemes() called, URL: http://localhost:3000/api/themes
🎨 [DEBUG] GET themes response: {status: 200, data: Array(4)}
✅ [DEBUG] Found 4 themes, data: [...]

4. Navigate to Theme Library

5. Verify in Network Tab

How API_BASE_URL Works

The API_BASE_URL injection token is configured in apps/gograb-space/src/app/app.config.ts:

// Provided in app.config.ts
{
  provide: API_BASE_URL,
  useValue: environment.backendApiUrl  // 'http://localhost:3000/api'
}

This value comes from apps/gograb-space/src/environments/environment.ts:

import { BACKEND_URLS } from './environment.shared';

export const environment = {
  useMockApi: false,
  backendApiUrl: BACKEND_URLS.local,  // 'http://localhost:3000/api'
};

And BACKEND_URLS.local is defined in environment.shared.ts:

export const BACKEND_URLS = {
  railway: 'https://backend-api-production-97c0.up.railway.app/api',
  local: 'http://localhost:3000/api',  // ← This value
  mock: '',
};

Architecture Lessons

✅ Always Use Injection Tokens for URLs

Good Pattern (used by all other services):

export class MyService {
  private readonly baseUrl = inject(API_BASE_URL);
  
  getData() {
    return this.http.get(`${this.baseUrl}/endpoint`);
  }
}

Bad Pattern (what ThemeHttpStorageService was doing):

export class MyService {
  getData() {
    return this.http.get('/api/endpoint'); // ❌ Relative URL
  }
}

Why Relative URLs Fail

When you use a relative URL like /api/themes:

Why Absolute URLs with DI Work

When you use ${this.baseUrl}/themes:

Verification Checklist

Related Fixes

This fix is part of a series to get the Theme Library working:

  1. Auth Fix (THEME_LIBRARY_FIX.md) - Made GET endpoints public
  2. Date Format Fix (THEME_LIBRARY_FIX.md) - Consistent ISO strings
  3. API Base URL Fix (this document) - Correct backend URL resolution

All three fixes were needed for the Theme Library to work properly!


Fixed: November 21, 2025 - 5:00 PM
Session: 11.25.1.1
Status: ✅ Complete and ready to test