# SSR/SSG Safe Implementation ## Overview This document describes the implementation of SSR/SSG safe patterns for the browser-web-apis package to prevent errors during server-side rendering and static site generation using **@angular/ssr** for robust platform detection. ## Problem Statement Browser APIs like `window`, `navigator`, `localStorage`, etc. are not available in server environments, causing errors like: - `window is not defined` - `navigator is not defined` - `localStorage is not defined` ## Solution Architecture ### 1. Enhanced SsrSafeUtil with @angular/ssr Integration Created a comprehensive utility that combines traditional detection with Angular's platform detection: ```typescript import { isPlatformBrowser, isPlatformServer } from '@angular/common'; import { Injectable, inject, PLATFORM_ID } from '@angular/core'; export class SsrSafeUtil { // Traditional detection (fallback) static isBrowser(): boolean { return typeof window !== 'undefined' && typeof document !== 'undefined'; } // Angular-based detection (preferred, requires injection context) static isAngularBrowser(): boolean { try { const platformId = inject(PLATFORM_ID); return isPlatformBrowser(platformId); } catch { return this.isBrowser(); } } static isAngularServer(): boolean { try { const platformId = inject(PLATFORM_ID); return isPlatformServer(platformId); } catch { return this.isServer(); } } } ``` ### 2. Injectable SsrSafeService Added an injectable service for contexts where DI is available: ```typescript @Injectable({ providedIn: 'root' }) export class SsrSafeService { private platformId = inject(PLATFORM_ID); get isBrowser(): boolean { return isPlatformBrowser(this.platformId); } get isServer(): boolean { return isPlatformServer(this.platformId); } get window(): Window | null { return this.isBrowser ? window : null; } get navigator(): Navigator | null { return this.isBrowser ? navigator : null; } } ``` ### 3. BrowserApiBaseService Enhancement Enhanced the base service to use Angular's platform detection: ```typescript export abstract class BrowserApiBaseService { protected platformId = inject(PLATFORM_ID); protected isBrowserEnvironment(): boolean { return isPlatformBrowser(this.platformId); } protected isServerEnvironment(): boolean { return isPlatformServer(this.platformId); } protected async initialize(): Promise { if (this.isServerEnvironment()) { this.logWarning(`${this.getApiName()} API not available in server environment`); return; } // ... rest of implementation } } ``` ## Service-Specific Changes with @angular/ssr ### BatteryService Updated to use Angular's platform detection: ```typescript @Injectable() export class BatteryService { private platformId = inject(PLATFORM_ID); private isBrowserEnvironment(): boolean { return isPlatformBrowser(this.platformId); } private isServerEnvironment(): boolean { return isPlatformServer(this.platformId); } private async initBattery(): Promise { if (this.initialized || !this.isBrowserEnvironment()) { return; } // ... rest of implementation } isSupported(): boolean { return this.isBrowserEnvironment() && 'getBattery' in navigator; } } ``` ## Implementation Patterns with @angular/ssr ### 1. Primary Pattern: Angular Platform Detection ```typescript // In services with DI protected platformId = inject(PLATFORM_ID); protected isBrowserEnvironment(): boolean { return isPlatformBrowser(this.platformId); } protected isServerEnvironment(): boolean { return isPlatformServer(this.platformId); } ``` ### 2. Static Methods with Fallback ```typescript // For contexts without DI static isAngularBrowser(): boolean { try { const platformId = inject(PLATFORM_ID); return isPlatformBrowser(platformId); } catch { return this.isBrowser(); // Fallback to traditional detection } } ``` ### 3. Injectable Service Pattern ```typescript @Injectable({ providedIn: 'root' }) export class SsrSafeService { private platformId = inject(PLATFORM_ID); get isBrowser(): boolean { return isPlatformBrowser(this.platformId); } } ``` ### 4. Safe API Access ```typescript // Use platform detection before accessing browser APIs if (this.isBrowserEnvironment()) { // Safe to access window, navigator, etc. const result = navigator.someAPI; } ``` ## Benefits of @angular/ssr Integration ### 1. **More Reliable Detection** - Uses Angular's internal platform detection - Handles edge cases better than simple `typeof window` checks - Consistent with Angular's own SSR handling ### 2. **Framework Integration** - Aligns with Angular's platform detection patterns - Works seamlessly with Angular Universal - Future-proof as Angular evolves ### 3. **Multiple Detection Strategies** - **Primary**: Angular's `isPlatformBrowser()` / `isPlatformServer()` - **Fallback**: Traditional `typeof window` checks - **Flexible**: Choose the right method for your context ### 4. **Type Safety** - Better TypeScript integration - Proper type guards - Reduced runtime errors ## Usage Guidelines ### For Services with DI (Recommended) ```typescript @Injectable() export class MyService { private platformId = inject(PLATFORM_ID); constructor() { if (isPlatformBrowser(this.platformId)) { // Browser-specific initialization } } } ``` ### For Static Contexts ```typescript export class MyUtil { static doSomething() { if (SsrSafeUtil.isAngularBrowser()) { // Browser-specific code } } } ``` ### For Components ```typescript @Component({...}) export class MyComponent { constructor(private ssrSafe: SsrSafeService) { if (this.ssrSafe.isBrowser) { // Browser-specific code } } } ``` ## Migration from Traditional Detection ### Before ```typescript if (typeof window !== 'undefined') { // Browser code } ``` ### After (Recommended) ```typescript const platformId = inject(PLATFORM_ID); if (isPlatformBrowser(platformId)) { // Browser code } ``` ### After (Fallback) ```typescript if (SsrSafeUtil.isAngularBrowser()) { // Browser code } ``` ## Testing Results - ✅ **Build successful** with @angular/ssr integration - ✅ **All tests passing** - ✅ **No TypeScript errors** - ✅ **Enhanced SSR/SSG compatibility** - ✅ **More reliable platform detection** ## Future Considerations 1. **Angular Universal Testing**: Test with actual SSR scenarios 2. **Performance**: Monitor performance impact of platform detection 3. **SSR Optimizations**: Explore server-side specific optimizations 4. **Edge Cases**: Handle hybrid rendering scenarios ## Conclusion The integration with **@angular/ssr** provides a more robust and reliable foundation for SSR/SSG safe operations. The combination of Angular's platform detection with traditional fallbacks ensures maximum compatibility while maintaining the benefits of framework-integrated detection. This approach is **production-ready** and follows Angular's best practices for SSR/SSG applications.