-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Overview
Implement dynamic home page routing based on user authentication status, similar to how GitHub serves different content on the root URL (/
) for authenticated vs non-authenticated users. This will be achieved using Cloudflare Service Bindings to connect our independently deployed workers.
Architecture Update
Important: The apps/edge/
package has been removed and consolidated into apps/api/
. We now have three independently deployable workers:
apps/web/
- Marketing site (static assets)apps/app/
- Main application (SPA with routes like /settings, /analytics)apps/api/
- Backend API (tRPC endpoints)
Current Behavior
The root URL (/
) is handled by the apps/web/
worker, which serves static marketing content regardless of authentication status.
Desired Behavior
- Unauthenticated users visiting
/
should see the marketing site (apps/web/dist/index.html
) - Authenticated users visiting
/
should see the main application dashboard (served byapps/app/
)
This creates a seamless GitHub-like experience where the home page intelligently adapts based on user state.
Technical Implementation
Approach: Web Worker as Authentication Router
The apps/web/
worker will be enhanced to act as an authentication-aware router for the home page, using service bindings to delegate to the appropriate worker.
1. Update apps/web/wrangler.jsonc
Add service bindings to connect to the app and API workers:
2. Create apps/web/worker.ts
Implement a worker that checks authentication and routes accordingly:
import { Hono } from 'hono';
import { createAuth } from '@repo/api/auth';
import { createDb } from '@repo/api';
import { getCookie } from 'hono/cookie';
interface Env {
APP_SERVICE: Fetcher;
API_SERVICE: Fetcher;
HYPERDRIVE_CACHED: Hyperdrive;
BETTER_AUTH_SECRET: string;
// ... other env vars
}
const app = new Hono<{ Bindings: Env }>();
// Home page routing logic
app.get('/', async (c) => {
try {
// Check if user has a session cookie
const sessionToken = getCookie(c, 'better-auth.session_token');
if (!sessionToken) {
// No session cookie, serve marketing site
return c.env.ASSETS.fetch(c.req.raw);
}
// Verify session with API service
const authCheckResponse = await c.env.API_SERVICE.fetch(
new Request('https://internal/api/auth/get-session', {
headers: {
'Cookie': `better-auth.session_token=${sessionToken}`,
'Content-Type': 'application/json'
}
})
);
if (authCheckResponse.ok) {
const { session } = await authCheckResponse.json();
if (session) {
// Valid session, proxy to app service
return c.env.APP_SERVICE.fetch(c.req.raw);
}
}
// Invalid session or error, serve marketing site
return c.env.ASSETS.fetch(c.req.raw);
} catch (error) {
// On any error, default to marketing site
console.error('Auth check failed:', error);
return c.env.ASSETS.fetch(c.req.raw);
}
});
// Serve all other routes from static assets
app.get('*', (c) => c.env.ASSETS.fetch(c.req.raw));
export default app;
3. Update apps/web/wrangler.jsonc
entry point
Change the main entry to use the worker:
{
"main": "./worker.ts", // Add this
"assets": {
"directory": "./dist"
},
// ... rest of config
}
4. Add TypeScript definitions
Create apps/web/types/env.d.ts
:
interface Env {
ASSETS: Fetcher; // Built-in for static assets
APP_SERVICE: Fetcher;
API_SERVICE: Fetcher;
BETTER_AUTH_SECRET: string;
ENVIRONMENT: string;
}
Alternative Approach: App Worker as Router
An alternative would be to have apps/app/
handle the routing logic and delegate to apps/web/
for unauthenticated users. This approach would:
- Keep authentication logic closer to the application
- Require updating routes in
apps/app/wrangler.jsonc
to capture/
- Use service binding to fetch marketing content from
apps/web/
Implementation Considerations
Performance
- Service bindings have zero latency overhead (run on same thread)
- Consider caching session validation results in Workers KV for faster subsequent checks
- Set appropriate cache headers based on authentication state
Security
- Ensure session validation cannot be bypassed
- Use secure cookie settings for session tokens
- Handle expired/invalid sessions gracefully
- Never expose internal service binding URLs
Deployment
- Service bindings require workers to be deployed in order
- First deploy
apps/api/
andapps/app/
- Then deploy
apps/web/
with service bindings configured - Use consistent service names across environments
Testing
- Test both authenticated and unauthenticated scenarios
- Verify proper fallback when services are unavailable
- Ensure session state is maintained across navigation
- Test with expired/invalid session tokens
Acceptance Criteria
- Unauthenticated users see marketing content at
/
- Authenticated users see application dashboard at
/
- Session validation is performed securely
- Proper error handling when services are unavailable
- No breaking changes to existing routes
- Performance impact is minimal (< 50ms additional latency)
- Works across all environments (dev, staging, production)
Resources
- Cloudflare Service Bindings Documentation
- Better Auth Session Management
- Hono Cookie Helper
- Current auth implementation:
apps/api/lib/auth.ts
Development Setup
-
Ensure all three workers are running locally:
bun web:dev # Port 5173 bun app:dev # Port 5174 bun api:dev # Port 5175
-
Test authentication flow:
- Visit
http://localhost:5173
(should see marketing site) - Log in via
/login
route - Visit
/
again (should see app dashboard) - Log out and verify
/
shows marketing site again
- Visit
Notes
- This implementation maintains the independence of all three workers
- Each worker can still be deployed separately
- Service bindings provide secure, performant inter-worker communication
- The approach mimics GitHub's seamless authenticated/unauthenticated experience
This is a great issue for learning:
- Cloudflare Workers service bindings
- Authentication flow in distributed systems
- Dynamic routing based on user state
- Modern edge computing patterns
Metadata
Metadata
Assignees
Labels
Type
Projects
Status