BACKEND UNIVERSE ENDPOINTS FIX
Documentation for Plans
Backend Universe Endpoints - Missing 404 Fix
Date: November 17, 2025
Status: ✅ COMPLETE (Needs Railway Deployment)
Priority: 🔥 CRITICAL
Problem Identified
Root Cause: Frontend was trying to sync universes to non-existent backend endpoints, resulting in continuous 404 errors:
PUT /api/spaces/69067c087ebb621cd9ff8c3f/universes/universe_1763422972924 → 404
DELETE /api/spaces/69067c087ebb621cd9ff8c3f/universes/universe_1762191946691 → 404
Why MongoDB wasn't updating: The backend had NO endpoints to handle universe CRUD operations within spaces. The frontend was correctly sending data, but it was hitting non-existent routes.
Solution Implemented
1. Added Universe CRUD Endpoints to SpacesController
File: apis/backend-api/src/app/spaces/spaces.controller.ts
// ========================================
// 🌌 UNIVERSE CRUD ENDPOINTS
// ========================================
/**
* Create universe in space
* POST /api/spaces/:spaceId/universes
*/
@Post(':spaceId/universes')
async createUniverse(
@Request() req: any,
@Param('spaceId') spaceId: string,
@Body() universeData: any,
): Promise<any> {
const userId = req.user.sub;
console.log('[SpacesController] 🌌 Creating universe:', { spaceId, universeId: universeData.id });
return this.spacesService.createUniverse(spaceId, userId, universeData);
}
/**
* Update universe in space
* PUT /api/spaces/:spaceId/universes/:universeId
*/
@Put(':spaceId/universes/:universeId')
async updateUniverse(
@Request() req: any,
@Param('spaceId') spaceId: string,
@Param('universeId') universeId: string,
@Body() universeData: any,
): Promise<any> {
const userId = req.user.sub;
console.log('[SpacesController] 🌌 Updating universe:', { spaceId, universeId });
return this.spacesService.updateUniverse(spaceId, userId, universeId, universeData);
}
/**
* Delete universe from space
* DELETE /api/spaces/:spaceId/universes/:universeId
*/
@Delete(':spaceId/universes/:universeId')
@HttpCode(HttpStatus.NO_CONTENT)
async deleteUniverse(
@Request() req: any,
@Param('spaceId') spaceId: string,
@Param('universeId') universeId: string,
): Promise<void> {
const userId = req.user.sub;
console.log('[SpacesController] 🗑️ Deleting universe:', { spaceId, universeId });
await this.spacesService.deleteUniverse(spaceId, userId, universeId);
}
2. Implemented Universe CRUD Methods in SpacesService
File: apis/backend-api/src/app/spaces/spaces.service.ts
// ========================================
// 🌌 UNIVERSE CRUD METHODS
// ========================================
/**
* Create universe in space
*/
async createUniverse(
spaceId: string,
userId: string,
universeData: any,
): Promise<any> {
// 1. Verify space ownership
// 2. Add universe ID to space.universeIds array
// 3. Save universe data to space database
// 4. Update space.updatedAt timestamp
}
/**
* Update universe in space
*/
async updateUniverse(
spaceId: string,
userId: string,
universeId: string,
universeData: any,
): Promise<any> {
// 1. Verify space ownership
// 2. Ensure universe ID is in space.universeIds array
// 3. Update universe data in space database
// 4. Update space.updatedAt timestamp
}
/**
* Delete universe from space
*/
async deleteUniverse(
spaceId: string,
userId: string,
universeId: string,
): Promise<void> {
// 1. Verify space ownership
// 2. Remove universe ID from space.universeIds array
// 3. Delete universe data from space database
// 4. Delete all universe collections
}
Key Features:
- ✅ Automatic
universeIdsmanagement: Automatically adds/removes universe IDs from the space document - ✅ Ownership verification: Ensures users can only modify their own spaces
- ✅ Data persistence: Stores universe metadata in space database
- ✅ Collection management: Cleans up universe collections on delete
3. Added Database Methods in SpaceDatabaseService
File: apis/backend-api/src/app/spaces/space-database.service.ts
/**
* Save universe data (metadata)
*/
async saveUniverseData(
spaceId: string,
universeId: string,
universeData: any
): Promise<void> {
const dbName = `space_${spaceId}`;
const db = this.connection.useDb(dbName);
const collection = db.collection('universes'); // Central universes metadata collection
await collection.updateOne(
{ _id: universeId as any },
{
$set: {
...universeData,
spaceId,
updatedAt: new Date()
},
$setOnInsert: {
createdAt: new Date()
}
},
{ upsert: true }
);
}
/**
* Delete universe data (metadata + collections)
*/
async deleteUniverseData(spaceId: string, universeId: string): Promise<void> {
// 1. Delete universe metadata from 'universes' collection
// 2. Delete all universe-specific collections (universe_{id}_pages, etc.)
}
Storage Architecture:
- Database:
space_{spaceId}(ONE per space) - Collection:
universes(Central metadata for all universes in the space) - Universe-specific collections:
universe_{id}_pages,universe_{id}_widgets, etc.
Expected Behavior After Deployment
Before (404 Errors):
Frontend: PUT /api/spaces/:spaceId/universes/:universeId
Backend: 404 Not Found
MongoDB: universeIds = [] (never updates)
After (Success):
Frontend: PUT /api/spaces/:spaceId/universes/:universeId
Backend: 200 OK (universe updated)
- Updates space.universeIds array
- Saves universe metadata
- Updates space.updatedAt
MongoDB: universeIds = ["universe_1763422972924"] ✅
Verification Steps
1. After Railway Deployment
Check Backend Logs (Railway Dashboard):
[SpacesController] 🌌 Updating universe: { spaceId: '69067c087ebb621cd9ff8c3f', universeId: 'universe_1763422972924' }
📥 Received universeIds to update: ["universe_1763422972924"]
✅ Updated space.universeIds to: ["universe_1763422972924"] (1 universes)
✅ Space saved to MongoDB: 69067c087ebb621cd9ff8c3f
✅ Universe updated: universe_1763422972924
2. Check MongoDB (Railway MongoDB Dashboard)
Space Document should show:
{
"_id": "69067c087ebb621cd9ff8c3f",
"name": "Xxb7jayp",
"spaceSlug": "chrome-space-1",
"ownerId": "68fbe3ac4740d821e041f9df",
"universeIds": ["universe_1763422972924"], // ✅ NOW POPULATED!
"settings": { ... },
"updatedAt": "2025-11-17T22:XX:XX.XXXZ" // ✅ NOW RECENT!
}
Space Database (space_69067c087ebb621cd9ff8c3f):
- Collection:
universesshould contain the universe metadata
3. Frontend Console Logs
[SyncEngine] 🔄 Syncing... { operations: 2 }
[SpaceApiService] 🌌 Updating universe: universe_1763422972924
[SpaceApiService] ✅ Universe updated (200 OK) // ✅ NO MORE 404!
[SpaceApiService] 🌌 Updating space: 68fbe3ac4740d821e041f9df
[SpaceApiService] ✅ Space updated (200 OK)
[SyncEngine] ✅ Synced { duration: "200ms" }
Files Changed
Backend Changes:
- ✅
apis/backend-api/src/app/spaces/spaces.controller.ts- Added universe CRUD endpoints - ✅
apis/backend-api/src/app/spaces/spaces.service.ts- Implemented universe CRUD methods - ✅
apis/backend-api/src/app/spaces/space-database.service.ts- Added database persistence methods
No Frontend Changes Needed:
- ✅ Frontend was already correctly calling the endpoints
- ✅ Frontend was already sending the correct data
- ✅ The issue was purely backend-side (missing routes)
Next Steps
Immediate (Required):
Deploy Backend to Railway:
# Railway auto-deploys on git push to main git add apis/backend-api/ git commit -m "feat: add universe CRUD endpoints to spaces API" git push origin mainMonitor Railway Deployment:
- Check build logs for errors
- Verify server starts successfully
- Check health endpoint:
https://your-backend.railway.app/api/health
Test Universe Creation:
- Create a new universe in GoGrab Space
- Watch frontend console for 200 OK (not 404)
- Check MongoDB for updated
universeIdsarray
Follow-up:
Multi-Browser Test:
- Create universe in Chrome
- Open Firefox with same user
- Verify universe appears in both browsers
Sync Verification:
- Create universe in Browser 1
- Wait 5 seconds (sync interval)
- Check Browser 2 for automatic update
Architecture Notes
Why This Structure?
Space Document (MongoDB gograb-api database):
{
"_id": "spaceId",
"universeIds": ["universe1", "universe2"], // ✅ Index/reference only
"settings": { ... }
}
Universe Data (MongoDB space_{spaceId} database):
// Collection: universes
{
"_id": "universe1",
"spaceId": "spaceId",
"name": "My Universe",
"description": "...",
// ... all universe metadata
}
Benefits:
- 🎯 Fast lookups:
space.universeIdsprovides instant list of user's universes - 📦 Data isolation: Each space has its own database for better organization
- 🔐 Security: Easy to verify ownership via
spaceId - 🚀 Scalability: Can move space databases to different servers
Lessons Learned
- Always check Railway logs - Frontend 404s often mean backend routes are missing
- Hybrid storage is complex - Space metadata in central DB, universe data in space-specific DB
- Frontend was never the problem - The sync system was working perfectly, just hitting non-existent endpoints
- MongoDB wasn't updating - Because the backend wasn't processing the requests at all
Status: ✅ Code complete, ⚠️ awaiting Railway deployment
Impact: 🔥 CRITICAL - Fixes universe persistence across browsers
Risk: ✅ LOW - Only adds new endpoints, doesn't modify existing behavior