If you have worked with the Firebase JS SDK or other Firebase client SDKs, you
are probably familiar with the `FirebaseApp` interface and how to use it to
configure app instances. To facilitate similar operations on the server side,
Firebase provides `FirebaseServerApp`.

`FirebaseServerApp` is a variant of `FirebaseApp` for use in server-side
rendering (SSR) environments. It includes tools to continue Firebase sessions
that span the client side rendering (CSR) / server-side rendering divide. These
tools and strategies can help enhance dynamic web apps built with Firebase and
deployed in Google environments like
[Firebase App Hosting](https://firebase.google.com/docs/app-hosting.md.txt).

Use `FirebaseServerApp` to:

- Execute server-side code within the *user* context, in contrast to the Firebase Admin SDK which has full administration rights.
- Enable the use of App Check in SSR environments.
- Continue a Firebase Auth session that was created in the client.

## The FirebaseServerApp lifecycle

Server-side rendering (SSR) frameworks and other non-browser runtimes such as
cloud workers optimize for initialization time by reusing resources across
multiple executions. `FirebaseServerApp` is designed to accommodate these
environments by using a reference count mechanism. If an app invokes
`initializeServerApp` with the same parameters as a previous
`initializeServerApp`, it receives the same `FirebaseServerApp` instance that
was already initialized. This cuts down on unnecessary initialization overhead
and memory allocations. When `deleteApp` is invoked on a `FirebaseServerApp`
instance, it reduces the reference count, and the instance is freed after the
reference count reaches zero.

### Cleaning up `FirebaseServerApp` instances

It can be tricky to know when to call `deleteApp` on a `FirebaseServerApp`
instance, especially if you are running many asynchronous operations in
parallel. The `releaseOnDeref` field of the `FirebaseServerAppSettings` helps
simplify this. If you assign `releaseOnDeref` a reference to an object with the
lifespan of the request's scope (for example, the headers object of the SSR
request), the `FirebaseServerApp` will reduce its reference count when the
framework reclaims the header object. This automatically cleans up your
`FirebaseServerApp` instance.

> [!NOTE]
> **Note:** The value `FirebaseServerApp.releaseOnDeref` is not used when `initializeServerApp` checks for an existing `FirebaseServerApp` instance with an identical configuration.

Here's an example usage of `releaseOnDeref`:

```javascript
/// Next.js
import { headers } from 'next/headers'
import { FirebaseServerAppSettings, initializeServerApp} from "firebase/app";

export default async function Page() {
  const headersObj = await headers();
  let appSettings: FirebaseServerAppSettings = {};
  appSettings.releaseOnDeref = headersObj;
  const serverApp = initializeServerApp(firebaseConfig, appSettings);
  ...
}
```

> [!NOTE]
> **Note:** Avoid mixing `deleteApp()` calls with instances that were created with `releaseOnDeref`; otherwise apps might be destroyed earlier than intended.

## Resume authenticated sessions created on the client

When an instance of `FirebaseServerApp` is initialized with an Auth ID token, it
enables bridging of authenticated user sessions between the client-side
rendering (CSR) and server-side rendering (SSR) environments. Instances of the
Firebase Auth SDK initialized with a `FirebaseServerApp` object containing an
Auth ID token will attempt to sign in the user on initialization without the
need for the application to invoke any sign-in methods.

Providing an Auth ID token allows apps to use any of Auth's sign-in methods on
the client, ensuring that the session continues on the server-side, even for
those sign-in methods that require user interaction. Additionally, it enables
the offloading of intensive operations to the server such as authenticated
Firestore queries, which should improve your app's rendering performance.

```javascript
/// Next.js
import { initializeServerApp } from "firebase/app";
import { getAuth } from "firebase/auth";

// Replace the following with your app's
// Firebase project configuration
const firebaseConfig = {
  // ...
};

const firebaseServerAppSettings = {
  authIdToken: token  // See "Pass client tokens to the server side
                      // rendering phase" for an example on how transmit
                      // the token from the client and the server.
}

const serverApp =
  initializeServerApp(firebaseConfig,
                      firebaseServerAppSettings);
const serverAuth = getAuth(serverApp);

// FirebaseServerApp and Auth will now attempt
// to sign in the current user based on provided
// authIdToken.
```

> [!NOTE]
> **Note:** Auth instances created with `FirebaseServerApp` don't support operations that log out the current user, sign in a new user, or otherwise replace the current user. Mutations to the Auth user state should be driven by the browser session.

## Use App Check in SSR environments

App Check enforcement relies on an App Check SDK instance that Firebase SDKs use
to internally call `getToken`. The resulting token is then included in requests
to all Firebase services, allowing the backend to validate the app.

However, because the App Check SDK needs a browser to access specific heuristics
for app validation, it can't be initialized in server environments.

`FirebaseServerApp` provides an alternative. If a client-generated App Check
token is provided during `FirebaseServerApp` initialization, it will be used by
the Firebase product SDKs when invoking Firebase services, eliminating the need
for an App Check SDK instance.

```javascript
/// Next.js
import { initializeServerApp } from "firebase/app";

// Replace the following with your app's
// Firebase project configuration
const firebaseConfig = {
  // ...
};

const firebaseServerAppSettings = {
  appCheckToken: token // See "Pass client tokens to the server side
                       // rendering phase" for an example on how transmit
                       // the token from the client and the server.
}

const serverApp =
  initializeServerApp(firebaseConfig,
                      firebaseServerAppSettings);

// The App Check token will now be appended to all Firebase service requests.
```

> [!NOTE]
> **Note:** If the token fails local verification due to expiration or parsing errors, then a console error is logged at the time of initialization of the `FirebaseServerApp` instance.

## Pass client tokens to the server-side rendering phase

To transmit authenticated Auth ID tokens (and App Check tokens) from the client
to the server-side rendering (SSR) phase, use a service worker. This approach
involves intercepting fetch requests that trigger SSR and appending the tokens
to the request headers.

Refer to [Session management with service
workers](https://firebase.google.com/docs/auth/web/service-worker-sessions) for a reference implementation
of a Firebase Auth service worker. Also see [Server side
changes](https://firebase.google.com/docs/auth/web/service-worker-sessions#server_side_changes) for code
that demonstrates how to parse these tokens from the headers for use in
`FirebaseServerApp` initialization.

> [!TIP]
> **Tip:** Refreshing the tokens within the service worker before appending them to the request headers is a best practice that ensures the SSR phase receives up-to-date credentials.

## Use Firestore in SSR environments

When building web applications with Server-side Rendering (SSR), you often need
to share data between the server and the client to optimize performance and user
experience. The Firestore SDK provides serialization tools that allow you to
capture snapshots and specific data types on the server and pass them directly
to your client-side components. This process eliminates redundant fetches by
enabling the client to hydrate state using the data pre-fetched during the SSR
phase. Additionally, you can transition from these serialized states to
real-time listeners, ensuring your application remains synchronized with the
database.

This section describes how to reuse data retrieved during the server-side
rendering (SSR) phase within client-side components.

### Serialize Data Types

Certain Firestore data types provide a `toJSON` method to convert their data
into a serializable format. These include instances of objects such as `Bytes`,
`GeoPoint`, `Timestamp` and `VectorValue`.

Once you have the data in JSON format, you can pass it from the server to the
client through standard framework mechanisms, or as parameters to components
that span the divide. For example:

```javascript
import {
  Bytes
} from 'firebase/firestore';

const BYTES_DATA = new Uint8Array([0, 1, 2, 3, 4, 5]);
const bytes = Bytes.fromUint8Array(BYTES_DATA);
const bytesJSON = bytes.toJSON();
```

### Deserialize Data Types

Firestore data types include the static method `fromJSON` to convert the
serialized data to an operable Firestore data type.

For example, the following deserializes a `Bytes` data type:

```typescript
import {
  Bytes
} from 'firebase/firestore';

// Assuming the same `bytesJSON` variable from the previous example.
const deserializedBytes = Bytes.fromJSON(bytesJSON);
```

### Serialize and deserialize Firestore Snapshots

Similar to Firestore data types, you can serialize instances of
`DocumentSnapshot` and `QuerySnapshot` using `toJSON`. However, to deserialize
them, you must use the standalone functions `documentSnapshotFromJSON` and
`querySnapshotFromJSON` instead of a static `fromJSON` method.

For example, the `querySnapshot` results of a `query` operation can be
serialized using the `toJSON` method:

```javascript
import {
  collection,
  getDocs,
  query,
  querySnapshotFromJSON
} from 'firebase/firestore';
// Assuming a configured instance of Firestore in the variable `firestore`.
const queryRef = query(collection(firestore, QUERY_PATH));
const querySnapshot = await getDocs(queryRef);
const querySnapshotJson = querySnapshot.toJSON();

```

Then, this data can be deserialized:

```javascript
import {
  querySnapshotFromJSON
} from 'firebase/firestore';

// deserializedSnapshot is an object of type QuerySnapshot:

const deserializedSnapshot =
  querySnapshotFromJSON(firestore, querySnapshotJson);
```

### Listeners with Serialized Snapshots

Although data queried during the SSR phase is valuable for your initial CSR
render, you might still need to monitor the Firestore service for real-time
updates to that information.

If your app requires these real-time updates, you can use the `onSnapshotResume`
function to initialize Firestore `SnapshotListener`s with serialized `Snapshot`
data. For example:

```javascript
const observer = {
  next: (qs) => {
    console.log("onSnapshot invoked: ", qs.data());
  },
  error: (e) => {
    console.log("error callback invoked: ", e.toString());
  }
};
const unsubscribe = onSnapshotResume(firestore, querySnapshotJson, observer);
```

> [!NOTE]
> **Note:** When a snapshot is loaded via `onSnapshotResume`, this deserialized data is **not** immediately updated to the query's local cache. Instead, the cache is maintained by any subsequent updates that occur through the active listener.