-
Notifications
You must be signed in to change notification settings - Fork 79
Add SSR Skill #80
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
maneesht
wants to merge
8
commits into
main
Choose a base branch
from
mtewani/add-ssr-skill
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Add SSR Skill #80
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a604ae8
Added ssr skills
maneesht 8abdc85
Created agent skills
maneesht 0bfd4b7
Updated nextjs advise
maneesht 7588713
Added releaseOnDeref
maneesht 57a9ff9
Update skills/firebase-ssr/SKILL.md
maneesht 3501907
Update skills/firebase-ssr/references/angular-ssr.md
maneesht 8ffb170
Updated auth
maneesht 67536dc
Added references to app check
maneesht File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| --- | ||
| name: firebase-ssr | ||
| description: "How to use Firebase in Server-Side Rendering (SSR) environments. Make sure to use this skill whenever the user mentions Next.js, Nuxt, SvelteKit, Angular SSR, Remix, or any other server-side framework, or asks about initializeServerApp, session cookies, fetching data on the server, or serializing Firebase data between server and client." | ||
| --- | ||
|
|
||
| # Firebase in SSR Environments | ||
|
|
||
| When building universal/SSR applications correctly, you must isolate Firebase apps to prevent cross-request state pollution and securely pass Firebase-specific data structures back to the client. | ||
|
|
||
| ## Framework Selection Workflow | ||
|
|
||
| The core concepts of Firebase SSR (request isolation and serialization mappings) apply to all major backend JS frameworks, but the execution syntax drastically changes. | ||
|
|
||
| **Step 1:** Identify the SSR framework the user is building with. | ||
|
|
||
| **Step 2:** Read the appropriate framework-specific reference guide before attempting to implement Firebase integration. | ||
| - `[references/nextjs.md](./references/nextjs.md)` - For Next.js App Router (RSCs, Route Handlers). | ||
| - `[references/remix.md](./references/remix.md)` - For Remix (`loader` / `action` functions). | ||
| - `[references/angular-ssr.md](./references/angular-ssr.md)` - For Angular Universal/SSR (`REQUEST` token and `TransferState`). | ||
|
|
||
| *Note: If the user's framework is not explicitly listed (e.g., SvelteKit, Nuxt), read `nextjs.md` mentally translating Next-specific concepts (like `headers()`) to the equivalent request handling method corresponding to their actual framework.* | ||
|
|
||
| --- | ||
|
|
||
| ## Core Principles | ||
|
|
||
| Regardless of the framework selected in Step 2, you must aggressively enforce the following core principles. | ||
|
|
||
| ### 1. Initializing Firebase: Avoid the Singleton | ||
|
|
||
| In a Node.js SSR context, utilizing the single `initializeApp` singleton is extremely dangerous because the server instance is shared across all incoming requests globally. | ||
|
|
||
| > [!WARNING] | ||
| > DO NOT use the standard `initializeApp()` inside server-side code that responds to HTTP endpoints or renders pages. It will cause severe data and authentication token leakage between different users. | ||
|
|
||
| Instead, use `initializeServerApp` to create a lightweight, request-scoped Firebase app instance. You can pass both the user's Auth ID token and an App Check token (if enabled) to authenticate the server-side requests on behalf of the client. | ||
|
|
||
| ```typescript | ||
| import { initializeServerApp } from "firebase/app"; | ||
|
|
||
| // Must be called for every incoming request handling routine | ||
| const app = initializeServerApp(firebaseConfig, { | ||
| authIdToken: extractedToken, // Provided by framework-specific headers | ||
| appCheckToken: extractedAppCheckToken // Optional, provided by framework-specific headers if App Check is enabled | ||
| }); | ||
| ``` | ||
|
|
||
| ### 2. Firestore Serialization Requirements | ||
|
|
||
| Data from `getFirestore` contains complex prototype objects (like `Timestamp`, `DocumentReference`, and `GeoPoint`) which cannot be natively serialized into JSON strings across network boundaries. | ||
|
|
||
| Always map over fetched Firestore documents to extract and convert these specific types to their serializable equivalents (such as `.toDate().toISOString()`) *before* returning them from the server component/loader. | ||
|
|
||
| ### 3. Data Connect Serialization Differences | ||
|
|
||
| Unlike Firestore, Firebase Data Connect utilizes standard GraphQL over its protocol. Responses to generated query functions are immediately returned as perfectly serializable JSON primitives. | ||
|
|
||
| Data fetched via Data Connect Server SDKs (`executeGraphql` or generated SDKs like `@firebasegen/default-connector`) does not require manual conversion of structures before being passed as page props or signals. | ||
|
|
||
| ### 4. Other Firebase Products (RTDB, Storage, Functions) | ||
|
|
||
| The `initializeServerApp` pattern is not limited to Firestore; you can safely initialize the client SDKs for Realtime Database, Cloud Storage, and Cloud Functions on the server. Because the app instance is authenticated via `authIdToken`, these calls will securely interact with Firebase infrastructure using the requesting user's identity. | ||
| - **Realtime Database**: Data returned from `get(ref(db, 'path'))` is already primitively structured (JSON serializable). | ||
| - **Cloud Storage**: You can safely fetch download URLs utilizing `getDownloadURL(ref(storage, 'path'))` on the server. | ||
| - **Cloud Functions**: You can securely execute callable functions using `httpsCallable(functions, 'name')(data)` on the server on behalf of the user. | ||
|
|
||
| ### 5. Resuming Server Context in the Client | ||
|
|
||
| Once you have initialized the server app and fetched data, it is a best-practice to seamlessly "resume" this state in the CSR (Client-Side Rendering) environment without generating a layout shift or making redundant network requests: | ||
|
|
||
| - **Firebase Auth Hydration**: Instead of rendering a blank or unauthenticated state while `onAuthStateChanged` initializes the client Firebase Auth SDK, pass the parsed user data (obtained from the decoded session cookie) as an initial property (e.g. `initialUser` prop in React, or via `TransferState` in Angular) to your client-side Auth Provider. | ||
| - **Firestore `onSnapshotResume`**: In Firebase JS v10+, if you initiate a Firestore query on the server (using `getDocs()` or `getDoc()`), you can pass the `.toJSON()` representation of that snapshot to the client. The client can then call `onSnapshotResume(db, serializedSnapshot, ...)` to immediately resume the listener from the server's state, preventing the client from re-downloading the initial snapshot. | ||
| - **Data Connect `subscribe`**: When executing generated queries on the server (e.g., `listMovies()`), the returned result object exposes a `.toJSON()` function. By passing this serialized representation to the client, you can hydrate initial UI state and supply it directly into the generated `subscribe(serializedQuery, ...)` function to resume watching for cache updates without re-triggering the initial query. | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this supposed to say "auth"? I didn't see any description of how to use initializeServerApp with Firestore above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I don't think I follow. Do you mean that it should say:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure, that was my best guess at what you meant, since Firestore wasn't mentioned.