10 KiB
Direct API Architecture Plan
Executive Summary
This document outlines a comprehensive plan to migrate from proxy-endpoint-based API calls to direct API calls in the Hensei SvelteKit application. This change will resolve current authentication and routing errors while simplifying the codebase and improving performance.
Key Problems Being Solved
- 404 Errors: Grid operations failing due to missing/incorrect proxy endpoints
- 401 Authentication Errors: Cookie-based auth not working properly through proxies
- Complexity: Maintaining duplicate routing logic in both adapters and proxy endpoints
- Performance: Extra network hop through proxy adds latency
- Inconsistency: Some operations use SSR, some use proxies, some try direct calls
Solution
Implement direct API calls from the browser to the Rails API using Bearer token authentication, which is the standard approach for modern SvelteKit applications.
Current Architecture Analysis
What We Have Now
Browser → SvelteKit Proxy (/api/*) → Rails API (localhost:3000/api/v1/*)
Current Authentication Flow
- User logs in via
/auth/login/+server.ts - OAuth token received and stored in httpOnly cookies
- Proxy endpoints read cookies and forward to Rails
- Rails uses Doorkeeper OAuth to authenticate via Bearer token
Current Problems
- Proxy Endpoints: 20+ proxy files in
/src/routes/api/ - Adapter Configuration: Uses
/apiin browser, expecting proxies that don't exist - Grid Operations: Broken due to missing/incorrect proxy endpoints
- URL Mismatches: Grid adapter uses wrong URL patterns
- Parameter Wrapping: Inconsistent parameter structures
Proposed Architecture
Direct API Calls
Browser → Rails API (localhost:3000/api/v1/*)
New Authentication Flow
- User logs in and receives OAuth token
- Store access token in:
- Server-side: httpOnly cookie (for SSR)
- Client-side: Memory/store (for browser requests)
- Include Bearer token in Authorization header for all API calls
- Rails authenticates directly via Doorkeeper
CORS Configuration
Rails already has proper CORS configuration:
origins %w[localhost:5173 127.0.0.1:5173]
credentials: true
methods: %i[get post put patch delete options head]
Implementation Plan
Phase 1: Update Authentication System
1.1 Create Token Store
Create /src/lib/stores/auth.store.ts:
import { writable, get } from 'svelte/store'
interface AuthState {
accessToken: string | null
user: UserInfo | null
expiresAt: Date | null
}
function createAuthStore() {
const { subscribe, set, update } = writable<AuthState>({
accessToken: null,
user: null,
expiresAt: null
})
return {
subscribe,
setAuth: (token: string, user: UserInfo, expiresAt: Date) => {
set({ accessToken: token, user, expiresAt })
},
clearAuth: () => {
set({ accessToken: null, user: null, expiresAt: null })
},
getToken: () => get(authStore).accessToken
}
}
export const authStore = createAuthStore()
1.2 Update Login Handler
Modify /src/routes/auth/login/+server.ts:
- Continue setting httpOnly cookies for SSR
- Also return access token in response for client storage
1.3 Update Root Layout
Modify /src/routes/+layout.svelte:
- Initialize auth store from page data
- Handle token refresh
Phase 2: Update Adapter System
2.1 Fix Adapter Configuration
Update /src/lib/api/adapters/config.ts:
export function getApiBaseUrl(): string {
// Always use direct API URL
const base = PUBLIC_SIERO_API_URL || 'http://localhost:3000'
return `${base}/api/v1`
}
2.2 Update Base Adapter
Modify /src/lib/api/adapters/base.adapter.ts:
protected async request<T>(
path: string,
options: RequestOptions = {}
): Promise<T> {
// Add Bearer token from auth store
const token = authStore.getToken()
const fetchOptions: RequestInit = {
...options,
credentials: 'include', // Still include for CORS
headers: {
'Content-Type': 'application/json',
...(token ? { 'Authorization': `Bearer ${token}` } : {}),
...(options.headers || {})
}
}
// ... rest of implementation
}
Phase 3: Fix Grid Adapter Issues
3.1 Fix DELETE Methods
Update /src/lib/api/adapters/grid.adapter.ts:
async deleteWeapon(params: { id: string; partyId: string }): Promise<void> {
return this.request<void>(`/grid_weapons/${params.id}`, {
method: 'DELETE'
})
}
// Similar for deleteCharacter and deleteSummon
3.2 Fix Position Update URLs
async updateWeaponPosition(params: UpdatePositionParams): Promise<GridWeapon> {
const { id, position, container } = params
return this.request<GridWeapon>(`/grid_weapons/${id}/update_position`, {
method: 'POST',
body: { position, container }
})
}
3.3 Fix Swap URLs
async swapWeapons(params: SwapPositionsParams): Promise<{
source: GridWeapon
target: GridWeapon
}> {
return this.request('/grid_weapons/swap', {
method: 'POST',
body: params
})
}
Phase 4: Remove Proxy Endpoints
Delete the entire /src/routes/api/ directory:
rm -rf /src/routes/api/
Phase 5: Update Services
5.1 Party Service
Update to pass auth token if needed for SSR:
class PartyService {
constructor(private fetch?: typeof window.fetch) {}
async getByShortcode(shortcode: string): Promise<Party> {
// On server, use fetch with cookies
// On client, adapter will use auth store
return partyAdapter.getByShortcode(shortcode)
}
}
Phase 6: Update Components
6.1 Party Component
No changes needed - already uses services correctly
6.2 Search Components
Update to use adapters directly instead of proxy endpoints
Files to Change
Core Authentication Files
/src/lib/stores/auth.store.ts- CREATE - Token storage/src/routes/auth/login/+server.ts- Return token in response/src/routes/+layout.svelte- Initialize auth store/src/routes/+layout.server.ts- Pass token to client
Adapter System Files
/src/lib/api/adapters/config.ts- Remove proxy logic/src/lib/api/adapters/base.adapter.ts- Add Bearer token support/src/lib/api/adapters/grid.adapter.ts- Fix all URL patterns and methods/src/lib/api/adapters/party.adapter.ts- Ensure proper auth/src/lib/api/adapters/search.adapter.ts- Ensure proper auth/src/lib/api/adapters/entity.adapter.ts- Ensure proper auth/src/lib/api/adapters/user.adapter.ts- Ensure proper auth
Service Files (Minor Updates)
/src/lib/services/grid.service.ts- Already correct/src/lib/services/party.service.ts- Already correct/src/lib/services/conflict.service.ts- Already correct
Component Files (No Changes)
/src/lib/components/party/Party.svelte- Already uses services- All grid components - Already use context correctly
Files to DELETE
/src/routes/api/- DELETE ENTIRE DIRECTORY
SSR Routes (No Changes Needed)
/src/routes/teams/[id]/+page.server.ts- Keep as-is/src/routes/teams/explore/+page.server.ts- Keep as-is- All other
+page.server.tsfiles - Keep as-is
Benefits of This Approach
Performance
- Eliminates proxy latency: Direct calls are faster
- Reduces server load: No proxy processing
- Better caching: Browser can cache API responses directly
Simplicity
- Less code: Remove 20+ proxy endpoint files
- Single source of truth: Adapters handle all API logic
- Standard pattern: Follows SvelteKit best practices
Reliability
- Fixes authentication: Bearer tokens work consistently
- Fixes routing: Direct URLs eliminate 404 errors
- Better error handling: Errors come directly from API
Developer Experience
- Easier debugging: Network tab shows actual API calls
- Less complexity: No proxy layer to understand
- Industry standard: What most SvelteKit apps do
Trade-offs and Considerations
Security Considerations
- API URL exposed: Browser can see Rails API URL (acceptable)
- Token in memory: XSS vulnerability (mitigated by httpOnly refresh token)
- CORS required: Must trust frontend origin (already configured)
Migration Risks
- Breaking change: All API calls will change
- Testing required: Need to test all operations
- Token management: Need to handle expiry/refresh
Mitigation Strategies
- Incremental rollout: Can update adapters one at a time
- Feature flags: Can toggle between old/new approach
- Comprehensive testing: Test each operation before removing proxies
Implementation Timeline
Day 1: Authentication System
- Create auth store
- Update login flow
- Test authentication
Day 2: Adapter Updates
- Update adapter configuration
- Add Bearer token support
- Fix Grid adapter URLs
Day 3: Testing & Cleanup
- Test all grid operations
- Test search, favorites, etc.
- Remove proxy endpoints
Day 4: Final Testing
- End-to-end testing
- Performance testing
- Documentation updates
Success Criteria
- All grid operations work: Add, update, delete, move, swap
- Authentication works: Login, logout, refresh
- No 404 errors: All API calls succeed
- No 401 errors: Authentication works consistently
- Performance improvement: Measurable latency reduction
Conclusion
This migration to direct API calls will:
- Solve immediate problems: Fix broken grid operations
- Improve architecture: Align with SvelteKit best practices
- Reduce complexity: Remove unnecessary proxy layer
- Improve performance: Eliminate proxy latency
- Enhance maintainability: Single source of truth for API logic
The approach is standard for modern SvelteKit applications and is what "9 out of 10 Svelte developers" would implement. It leverages the existing CORS configuration in Rails and uses industry-standard Bearer token authentication.
Next Steps
- Review and approve this plan
- Create auth store implementation
- Update adapters incrementally
- Test thoroughly
- Remove proxy endpoints
- Deploy with confidence
Document created: November 2024 Author: Claude Assistant Status: Ready for Implementation