Skip to content

FB4D Reference IRealTimeDB

Christoph Schneider edited this page Dec 22, 2023 · 26 revisions

Interface IRealTimeDB

This chapter mainly only describes the synchronous method calls for the convenience of description. The asynchronous method calls are identical but with additional callback routines for success and failure.

Create an instance for the interface IRealTimeDB

The interface will be created by the constructor of the class TRealTimeDB in the unit FB4D.RealTimeDB. The new constructor CreateByURL expects as parameters the Firebase URL of the Realtime Database and the instance of the interface IFirebaseAuthentication that is in the state signed-in. You find your FirebaseURL in the Firebase Console in the Realtime Database/Data view in the header after the link icon.

var
  Database: IRealTimeDB;
begin
  Database := TRealTimeDB.CreateByURL(<FirebaseURL: string>, <IFirebaseAuthentication>);

Hint to Deprecated Constructor

There was a change in the Firebase console on how to create real-time databases. Previously the Firebase URL was derivable from the project ID. Therefore the former constructor TRealTimeDB.Create is marked as deprecated and should not be used anymore.

Alternatively, you can use the class factory TFirebaseConfiguration.RealTimeDB to get an instance of IRealTimeDB.

Read a JSON Node

The Get method reads a JSON node and returns it with all subnodes for a given JSON path. For this purpose, the node path is passed in the ResourceParams as a dynamic string array (e.g. ['Top', 'Mid', 'Low'] to build the following JSON path: '/Top/Mid/Low').

As a result, the caller receives a TJSONValue class, which can be a derived TJSONObject for a JSON node that contains more than one field or subnodes. Note that the caller has to free this TJSONValue to avoid memory leaks.

function GetSynchronous(ResourceParams: TRequestResourceParam;
  QueryParams: TQueryParams = nil): TJSONValue; 

The QueryParams can optionally be used to sort the result with Order-By or to limit the range of the result. The following code excerpt shows an application for using the asynchronous Get method call along with the optional arguments in QueryParams.

procedure TMyForm.GetRealTimeNodes(const TopNodeName, MidNodeName, 
  SortNodeName: string; NumberOfFirstRow, NumberOfLastRow: integer);
var
  Query: TQueryParams;
begin
  Query := TQueryParams.Create;
  try
    Query.AddOrderBy(SortNodeName);
    Query.AddLimitToFirst(NumberOfFirstRow);
    Query.AddLimitToLast(NumberOfLastRow);
    fRealTimeDB.Get([TopNodeName, MidNodeName], OnGetResponse, OnGetError, Query);
  finally
    Query.Free;
  end; 
end;

procedure TMyForm.OnGetResponse(ResourceParams: TRequestResourceParam;
  Val: TJSONValue); ...

procedure TMyForm.OnGetError(const RequestID, ErrMsg: string); ...

It is important that the nodes do not necessarily appear in the desired order in the resulting JSON, even with an OrderBy. In combination with LimitToFirst or LimitToLast, however, the desired nodes are always returned.

Filter by a specific field

In cases where you use the same structures for a lot of data, you can use data filtering to read out only a subset of the nodes. E.g. your database contains the following tree in the top node Chat:

Chat
> -NDX92PHk-M-qf3TZUDd
--> Ind: 1 
--> Msg: "First chat message"
> -Nk-Hl4_TP-wOrB4D2_4
--> Ind: 2 
--> Msg: "Second chat message"
> -NwE72Wh_MQPYqlD4-1a
--> Ind: 3 
--> Msg: "Third chat message"

A query built with TQueryParams.CreateQueryParams.AddOrderByAndEqualTo('Ind', 2) allows to retrieve just the second node:

-Nk-Hl4_TP-wOrB4D2_4: {Ind: 2, Msg: "Second chat message"}

The TQueryParams helper class offers this method for 3 different filter value types: string, integer and float.

Write a JSON Node

The Put method writes or overwrites an entire JSON node with all its child nodes for a given JSON path.

function PutSynchronous(ResourceParams: TRequestResourceParam;
  Data: TJSONValue; QueryParams: TQueryParams = nil): TJSONValue; 

The following code excerpt shows an application for using the asynchronous Put method call.

procedure TMyForm.GetRealTimeNodes(const TopNodeName, MidNodeName, 
  FieldAVal, FieldBVal: string);
var
  Data: TJSONObject;
begin
  Data := TJSONObject.Create;
  try
    Data.AddPair('FieldA', FieldAVal);
    Data.AddPair('FieldB', FieldBVal);
    fRealTimeDB.Put([TopNodeName, MidNodeName], Data, OnPutResponse, OnPutError);
  finally
    Data.Free;
  end;
end;

procedure TMyForm.OnPutResp(ResourceParams: TRequestResourceParam;
  Val: TJSONValue); ...

procedure TMyForm.OnPutError(const RequestID, ErrMsg: string); ...

Delete a JSON Node

To delete an entire node in the JSON tree you must use the Delete method. In the ResourceParams pass the desired JSON path as a dynamic string array.

function DeleteSynchronous(ResourceParams: TRequestResourceParam;
  QueryParams: TQueryParams = nil): boolean;

Write a New Node by using an Automatically Generated ID

The Post method is comparable to an auto-increment index in relational databases. It creates a subnode with a unique ID.

function PostSynchronous(ResourceParams: TRequestResourceParam;
  Data: TJSONValue; QueryParams: TQueryParams = nil): TJSONValue; 

Note that the caller has to free this TJSONValue to avoid memory leaks.

Change the content of an existing node

The Put method does not allow to change the content of the value of a node that contains sub-nodes without lost of sub-nodes. For this purpose, the method Patch must be used.

function PatchSynchronous(ResourceParams: TRequestResourceParam;
  Data: TJSONValue; QueryParams: TQueryParams = nil): TJSONValue; 

Note that the caller has to free this TJSONValue to avoid memory leaks.

Install a Listener for Changes in a JSON Node

Install a listener to be notified of any changes in and under a particular JSON node. The following function starts a long-running REST call that receives a message by any data change on the registered node. This is a much better approach than polling a JSON node with repeated calls of the method Get because much less data is required to transfer between the cloud database and the client application.

function ListenForValueEvents(ResourceParams: TRequestResourceParam;
  ListenEvent: TOnReceiveEvent; OnStopListening: TOnStopListenEvent;
  OnError: TOnRequestError;
  OnAuthRevoked: TOnAuthRevokedEvent = nil;
  DoNotSynchronizeEvents: boolean = false): IFirebaseEvent;

The function returns an interface to IFirebaseEvent to stop the listener again.

You can also start multiple listeners, but each needs its own IFirebaseEvent variable. As soon as the IFirebaseEvent is freed, the listener thread is automatically terminated.

The callback function ListenEvent will be called when there is a data change with the node addressed by the database path passed in ResourceParams. With the default value DoNotSynchronizeEvents=false the functions ListenEvent, OnError, and OnAuthRevoked are called in the main thread to ensure that the GUI can be accessed within these events. If there is no GUI access in the events and for possible database access, a separate database session is available, the necessary synchronization can be omitted with DoNotSynchronizeEvents=true.

TOnReceiveEvent = procedure(const Event: string;
  Params: TRequestResourceParam; JSONObj: TJSONObject) of object;

In the event parameter string, we expect either put when the entire node should be replaced on the client-side. A patch will be received if the data in the resulting JSON is just a part of the monitored JSON object. The part concerns the relative database path. A cancel will be received in case of unexpected errors. For more information about cancel see also the Firebase Documentation.

The JSONObj contains for put and patch the following data JSON object:

data: {"path": <RelativeDBpath>, "data": {<JSONObjectofNodeData>}}

The following constants have been defined in FB4D.Interfaces to compare the event and to read the JSONObj:

const
  cEventPut = 'put';
  cEventPatch = 'patch';
  cEventCancel = 'cancel';
  cData = 'data';
  cPath = 'path';

The callback function OnStopListening will be called when the listener was stopped. This happens as a result if the listener was stopped by the application (via IFirebaseEvent.StopListening) or because the internet connection was interrupted. In such a case, you should restart the listener when the internet connection becomes available again. Additional to the OnStopListening you will get a callback at the function 'OnError' to be informed about the concrete error reason.

The optional callback function 'OnAuthRevoked' informs you when the authentication token was renewed automatically by the listener.

Hint: The former method function IRealTimeDB.GetLastKeepAliveTimeStamp: TDateTime is replaced by the new method IFirebaseEvent.GetLastReceivedMsg.

Get a Server Variable

With this method, a server variable (the server timestamp which is number as Unix formatted timestamp) can be written into a node of the given database path. As result, the server time will be returned.

function GetServerVariablesSynchronous(const ServerVarName: string;
  ResourceParams: TRequestResourceParam): TJSONValue;
Clone this wiki locally