Skip to content

Commit bbdcc3c

Browse files
authored
Implement Realtime Autofetch on Config Update Notifications (#9206)
Adds logic to listen for realtime update notifications. When a new template version is detected, it automatically fetches the config. After a successful fetch, a callback is triggered to notify the app that a new config version has been retrieved and is ready to be activated.
1 parent a1ccd9a commit bbdcc3c

16 files changed

+1548
-60
lines changed

.changeset/lemon-baboons-lick.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@firebase/remote-config': minor
3+
'firebase': minor
4+
'@firebase/remote-config-types': minor
5+
---
6+
7+
Added support for Realtime Remote Config for the web. This feature introduces a new `onConfigUpdate` API and allows web applications to receive near-instant configuration updates without requiring periodic polling.

common/api-review/remote-config.api.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ export interface FetchResponse {
4242
config?: FirebaseRemoteConfigObject;
4343
eTag?: string;
4444
status: number;
45+
templateVersion?: number;
4546
}
4647

4748
// @public
4849
export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle';
4950

51+
// @public
52+
export type FetchType = 'BASE' | 'REALTIME';
53+
5054
// @public
5155
export interface FirebaseRemoteConfigObject {
5256
// (undocumented)
@@ -78,7 +82,7 @@ export function isSupported(): Promise<boolean>;
7882
export type LogLevel = 'debug' | 'error' | 'silent';
7983

8084
// @public
81-
export function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Promise<Unsubscribe>;
85+
export function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Unsubscribe;
8286

8387
// @public
8488
export interface RemoteConfig {

docs-devsite/remote-config.fetchresponse.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface FetchResponse
2727
| [config](./remote-config.fetchresponse.md#fetchresponseconfig) | [FirebaseRemoteConfigObject](./remote-config.firebaseremoteconfigobject.md#firebaseremoteconfigobject_interface) | Defines the map of parameters returned as "entries" in the fetch response body.<p>Only defined for 200 responses. |
2828
| [eTag](./remote-config.fetchresponse.md#fetchresponseetag) | string | Defines the ETag response header value.<p>Only defined for 200 and 304 responses. |
2929
| [status](./remote-config.fetchresponse.md#fetchresponsestatus) | number | The HTTP status, which is useful for differentiating success responses with data from those without.<p>The Remote Config client is modeled after the native <code>Fetch</code> interface, so HTTP status is first-class.<p>Disambiguation: the fetch response returns a legacy "state" value that is redundant with the HTTP status code. The former is normalized into the latter. |
30+
| [templateVersion](./remote-config.fetchresponse.md#fetchresponsetemplateversion) | number | The version number of the config template fetched from the server. |
3031

3132
## FetchResponse.config
3233

@@ -65,3 +66,13 @@ The HTTP status, which is useful for differentiating success responses with data
6566
```typescript
6667
status: number;
6768
```
69+
70+
## FetchResponse.templateVersion
71+
72+
The version number of the config template fetched from the server.
73+
74+
<b>Signature:</b>
75+
76+
```typescript
77+
templateVersion?: number;
78+
```

docs-devsite/remote-config.md

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ The Firebase Remote Config Web SDK. This SDK does not work in a Node.js environm
5353
| Type Alias | Description |
5454
| --- | --- |
5555
| [FetchStatus](./remote-config.md#fetchstatus) | Summarizes the outcome of the last attempt to fetch config from the Firebase Remote Config server.<ul> <li>"no-fetch-yet" indicates the [RemoteConfig](./remote-config.remoteconfig.md#remoteconfig_interface) instance has not yet attempted to fetch config, or that SDK initialization is incomplete.</li> <li>"success" indicates the last attempt succeeded.</li> <li>"failure" indicates the last attempt failed.</li> <li>"throttle" indicates the last attempt was rate-limited.</li> </ul> |
56+
| [FetchType](./remote-config.md#fetchtype) | Indicates the type of fetch request.<ul> <li>"BASE" indicates a standard fetch request.</li> <li>"REALTIME" indicates a fetch request triggered by a real-time update.</li> </ul> |
5657
| [LogLevel](./remote-config.md#loglevel) | Defines levels of Remote Config logging. |
5758
| [Unsubscribe](./remote-config.md#unsubscribe) | A function that unsubscribes from a real-time event stream. |
5859
| [ValueSource](./remote-config.md#valuesource) | Indicates the source of a value.<ul> <li>"static" indicates the value was defined by a static constant.</li> <li>"default" indicates the value was defined by default config.</li> <li>"remote" indicates the value was defined by fetched config.</li> </ul> |
@@ -295,7 +296,7 @@ Starts listening for real-time config updates from the Remote Config backend and
295296
<b>Signature:</b>
296297

297298
```typescript
298-
export declare function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Promise<Unsubscribe>;
299+
export declare function onConfigUpdate(remoteConfig: RemoteConfig, observer: ConfigUpdateObserver): Unsubscribe;
299300
```
300301

301302
#### Parameters
@@ -307,7 +308,7 @@ export declare function onConfigUpdate(remoteConfig: RemoteConfig, observer: Con
307308

308309
<b>Returns:</b>
309310

310-
Promise&lt;[Unsubscribe](./remote-config.md#unsubscribe)<!-- -->&gt;
311+
[Unsubscribe](./remote-config.md#unsubscribe)
311312

312313
An [Unsubscribe](./remote-config.md#unsubscribe) function to remove the listener.
313314

@@ -384,6 +385,18 @@ Summarizes the outcome of the last attempt to fetch config from the Firebase Rem
384385
export type FetchStatus = 'no-fetch-yet' | 'success' | 'failure' | 'throttle';
385386
```
386387

388+
## FetchType
389+
390+
Indicates the type of fetch request.
391+
392+
<ul> <li>"BASE" indicates a standard fetch request.</li> <li>"REALTIME" indicates a fetch request triggered by a real-time update.</li> </ul>
393+
394+
<b>Signature:</b>
395+
396+
```typescript
397+
export type FetchType = 'BASE' | 'REALTIME';
398+
```
399+
387400
## LogLevel
388401

389402
Defines levels of Remote Config logging.

packages/remote-config/src/api.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ export function getRemoteConfig(
6868
rc._initializePromise = Promise.all([
6969
rc._storage.setLastSuccessfulFetchResponse(options.initialFetchResponse),
7070
rc._storage.setActiveConfigEtag(options.initialFetchResponse?.eTag || ''),
71+
rc._storage.setActiveConfigTemplateVersion(
72+
options.initialFetchResponse.templateVersion || 0
73+
),
7174
rc._storageCache.setLastSuccessfulFetchTimestampMillis(Date.now()),
7275
rc._storageCache.setLastFetchStatus('success'),
7376
rc._storageCache.setActiveConfig(
@@ -100,6 +103,7 @@ export async function activate(remoteConfig: RemoteConfig): Promise<boolean> {
100103
!lastSuccessfulFetchResponse ||
101104
!lastSuccessfulFetchResponse.config ||
102105
!lastSuccessfulFetchResponse.eTag ||
106+
!lastSuccessfulFetchResponse.templateVersion ||
103107
lastSuccessfulFetchResponse.eTag === activeConfigEtag
104108
) {
105109
// Either there is no successful fetched config, or is the same as current active
@@ -108,7 +112,10 @@ export async function activate(remoteConfig: RemoteConfig): Promise<boolean> {
108112
}
109113
await Promise.all([
110114
rc._storageCache.setActiveConfig(lastSuccessfulFetchResponse.config),
111-
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag)
115+
rc._storage.setActiveConfigEtag(lastSuccessfulFetchResponse.eTag),
116+
rc._storage.setActiveConfigTemplateVersion(
117+
lastSuccessfulFetchResponse.templateVersion
118+
)
112119
]);
113120
return true;
114121
}
@@ -369,12 +376,12 @@ export async function setCustomSignals(
369376
*
370377
* @public
371378
*/
372-
export async function onConfigUpdate(
379+
export function onConfigUpdate(
373380
remoteConfig: RemoteConfig,
374381
observer: ConfigUpdateObserver
375-
): Promise<Unsubscribe> {
382+
): Unsubscribe {
376383
const rc = getModularInstance(remoteConfig) as RemoteConfigImpl;
377-
await rc._realtimeHandler.addObserver(observer);
384+
rc._realtimeHandler.addObserver(observer);
378385
return () => {
379386
rc._realtimeHandler.removeObserver(observer);
380387
};

0 commit comments

Comments
 (0)