Migration guide

Updated

Migrate from Signaling 1.x to Signaling 2.x

This migration guide helps you migrate from Signaling 1.x to Signaling 2.x.

In December 2023, Agora released Signaling 2.x in response to market and industry needs. Signaling 2.x brings significant innovation to users in terms of:

  • Features coverage: Signaling 2.x expands its scope to encompass a broader range of business use-cases through the introduction of functional modules including Channel, Message, Topic, Presence, Storage, and Lock. These additions empower you to shift your attention from fundamental feature implementation to fostering business innovation.

  • Performance improvement: Signaling 2.x revamps the backend architecture, enhancing overall performance through optimized network connections. This overhaul results in sustained low-latency, higher reliability, increased concurrency, and seamless scalability for real-time network access. With these advancements, businesses can confidently rely on the backend, to ensure both optimal performance and high-quality service.

  • Experience optimization: All documents, including user guides and API references, have been optimized and furnished more comprehensive sample code. These enhancements empower developers with a cost-effective learning experience, enable swift integration and improve development efficiency when utilizing the SDK.

Agora has redesigned and simplified the API interface to accommodate the industry's most popular Async/Await programming model.

For a seamless transition from Signaling 1.x to 2.x, refer to the following document. This resource is designed to expedite the migration process, ensuring that you can swiftly leverage the new features in Signaling 2.x and start reaping their benefits.

Activate Signaling

To activate Signaling 2.x, take the following steps:

  1. Log in to Agora Console
  2. Create a new Agora project or choose an existing project from the project list.
  3. Select the project on the Project Management page and click the pencil icon to configure it.
  4. Go to All features > Signaling > Basic information and select a data center in the dropdown.
  5. Go to Subscriptions > Signaling and subscribe to a plan.
  6. Copy the App ID for your project for use in your code.

Signaling version 2.x differs from 1.x in the support of naming character sets. For example, 2.x does not support channel names, user names, or topic names that start with _ or contain . characters. When upgrading from 1.x to 2.x or using both versions together, be aware of possible incompatibilities caused by character set differences. Best practice is to use the character set supported by 2.x when migrating. See Channel naming for the character set supported by 2.x.

Integrate the SDK

The SDK package names for Signaling 2.x and 1.x are different. Both versions, however, support integration via CDN and npm.

To use CDN, directly add the following code to your web application or download the SDK file for local reference.

Signaling 2.x integration:

// Replace x.y.z with the specific SDK version number, such as 2.2.0
<script src="your_path_to_sdk/agora-rtm.x.y.z.min.js"></script>
  1. Install the SDK via npm package manager in your terminal.

    npm install agora-rtm-sdk

  2. Import it into your app.js.

    import AgoraRTM from 'agora-rtm-sdk';

Initialize a client instance

Compared to 1.x, Signaling 2.x makes significant adjustments to the initialization parameters, adding many new features such as client-side encryption and cloud proxy. For details, see the API Reference. Additionally, Signaling 2.x optimizes error handling within the try {...} catch {...} programming model by categorizing potential errors and using the ErrorInfo data structure for callbacks, helping you troubleshoot issues more efficiently. By referencing the Error Codes table, you can quickly find the cause of an error and its solution.

  • Sample code for Signaling 1.x:
    let options = {
      uid: "",
      token: ""
    }
    // Fill in the App ID you obtained from the console
    const appID = "<Your app ID>";
    // Fill in your user ID
    options.uid = "<Your uid>";
    // Fill in your token
    options.token = "<Your token>";
    // Initialize the client instance
    const rtm = AgoraRTM.createInstance(appID);
  • Sample code for Signaling 2.x:
    // Initialize the client instance
    const { RTM } = AgoraRTM;
    // Fill in the App ID you obtained from the console
    const appId = "your_appId";
    // Fill in your user ID
    const userId = "your_userId";

    try {
      const rtm = new RTM(appId, userId);
    } catch (status) {
      console.log(status);
    }

Log in to Signaling

Signaling 2.x adopts the standard Async/Await pattern, enabling support for asynchronous programming. The method for logging into Signaling in 2.x differs from 1.x, as follows:

  • Sample code for Signaling 1.x:
    await rtm.login(options)
  • Sample code for Signaling 2.x:
    const token = 'your_token'
    try {
      const result = await rtm.login({ token });
      console.log(result);
    } catch (status) {
      console.log(status);
    }

Event notifications

Signaling 2.x redesigns the system event notification model and API interface to offer a more detailed classification and aggregation of event notification types. It also optimizes the payload data structure of event notifications.

The following table lists the types of event notifications in Signaling 2.x:

Event TypeDescription
messageMessage Event Notification: Receives all message event notifications from the Message channel and Topic that the user subscribes to.
presencePresence Event Notification: Receives all presence event notifications from the Message channel and Stream channel that the user subscribes to or joins.
topicTopic Change Event Notification: Receives all topic change notifications from the Stream channel that the user joins.
storageChannel and User Metadata Event Notification: Receives all channel metadata event notifications from the Message channel and Stream channel, and user metadata event notifications from subscribed users.
lockLock Event Notification: Receives all lock event notifications from the Message channel and Stream channel that the user subscribes to or joins.
statusNetwork Connection Status Change Notification: Receives notifications about changes in the client's network connection status.
linkStateReceives notifications about changes in the client's network connection status, including information on the state before and after the change, service type, operation type, reason for the change, and channel list.
tokenPrivilegeWillExpireReceives notifications about the impending expiration of the client's token.

For more information on event notifications and payload data structures, see Event Listeners.

The following example shows how to listen for message event notifications:

  • Signaling 1.x:
    let channel = rtm.createChannel("demoChannel");
    channel.on('ChannelMessage', function (message, memberId) {
      console.log(memberId + ": " + message.text);
    });
  • Signaling 2.x:
    rtm.addEventListener("message", event => {
      const channelType = event.channelType;
      const channelName = event.channelName;
      const topic = event.topicName;
      const messageType = event.messageType;
      const customType = event.customType;
      const publisher = event.publisher;
      const message = event.message;
      const timestamp = event.timestamp;
    });

Notice the following significant differences:

  • In 1.x, message event notifications are bound to a specific channel instance. Users need to first call the createChannel() method to create a channel instance, and then bind event handlers using channel.on(). Multiple channels require multiple bindings. In Signaling 2.x, message event notifications are bound to the client instance and are set globally. You register callbacks with the addEventListener() method, binding only once to listen to all subscribed channels or topics.

  • The payload data structure for message event notifications in 1.x contains limited information, while Signaling 2.x's payload data structure includes more detailed information, helping you implement your business logic more effectively.

Channel messages

In 1.x, sending channel messages involves the following steps:

  1. Create a channel instance
  2. Join the channel
  3. Send a channel message

The drawback of this design is that you cannot send a message without receiving it, as sending and receiving messages are coupled. Signaling 2.x adopts a new Pub/Sub-based design, decoupling sending and receiving channel messages. You can send messages to a specified channel without joining it, and you can receive messages from a channel simply by subscribing to it. These operations are independent of each other.

  • The sample code for 1.x is as follows:
    let channel = rtm.createChannel("demoChannel");
    await channel.join();
    let payload = "Hello World!";
    await channel.sendMessage({ text: payload }).then(() => {
        console.log("Channel message:" + payload + " from " + channel.channelId);
    }
  • The sample code for Signaling 2.x is as follows:
    // Send channel message
    const payload = "Hello World!";
    try {
        const result = await rtm.publish("demoChannel", payload, { channelType: 'MESSAGE' });
        console.log(result);
    } catch (status) {
        console.log(status);
    }

    // Subscribe to a channel
    try {
        const result = await rtm.subscribe("chat_room");
        console.log(result);
    } catch (status) {
        console.log(status);
    }

Peer-to-peer messaging

In version 1.x, the peer-to-peer messaging API is used to send messages to a specified user. For example, to send a message to a user with user ID "Tony":

// v1
rtm.sendMessageToPeer(
  { text: "test peer message" }, // RtmMessage instance
  "PeerId" // remote user ID
)
.then((sendResult) => {
  if (sendResult.hasPeerReceived) {
    // Implement logic for when the remote user has received the message
  } else {
    // Implement logic for when the server has received the message but the peer has not
  }
})
.catch((error) => {
  // Implement logic for peer-to-peer message send failure
});

Starting from v2.2.1, Signaling 2.x supports User Channels for peer-to-peer messaging. Use the publish() method with channelType set to USER to send messages to a specific user. Handle incoming messages in the message event handler. For more information, see User channels.

// v2.2.1 and later
rtm
  .publish(
    "test peer message",
    "PeerId", // remote user ID
    { channelType: "USER" }
  )
  .then((sendResult) => {
    // Implement logic for when the remote user receives the message
  })
  .catch((error) => {
    // Implement logic for publish failure
  });

To receive peer-to-peer messages, implement the message event handler. For User Channel messages, the channel type in the event payload is set to USER.

Picture and file messages

For reasons of user data privacy, compliance and cost optimization, Signaling 2.x no longer directly supports image and file message types. After version 1.5.0, the related interfaces have been deprecated. However, this does not mean that you cannot use Signaling to transmit and distribute image and file messages. You can build image and file message functions with the help of Signaling 2.x and third-party object storage services, such as Amazon S3 or Alibaba Cloud OSS. Not only can you get the best factual message transmission experience, you can also implement more flexible technical solutions. For example, CDN static resource acceleration or image and text moderation. The following code sample shows you how to use Signaling 2.x and Amazon S3 object storage service to build and send image and file messages.

const sendFileMessageAsync = async () => {
  try {
    // Use the document-picker component to select a file from the device
    const result = await DocumentPicker.getDocumentAsync({
      // You can choose any type of file
      type: "*/*",
    });

    if (result.canceled === false) {
      const file = result.assets[0].file;
      if (file) {
        const uploadParams = {
          // Fill in your bucket name on Amazon S3
          Bucket: "your-awsbucket",
          // The key for the file on Amazon S3
          Key: file.name,
          Body: file,
          ContentType: file.type,
        };
        // Upload the file to S3
        s3.upload(uploadParams, (err, data) => {
          if (err) {
            console.error("Error uploading file:", err);
          } else {
            console.log("File uploaded successfully. S3 URL:", data.Location);
            // After the file is uploaded successfully, create a custom file message payload
            const imageMessagePayload = {
              // File type; the receiver can parse the message structure based on this field
              type: "File",
              // Your bucket name on Amazon S3; the receiver needs this to download the file
              bucket: uploadParams.Bucket,
              // The key of the file stored on Amazon S3; the receiver needs this to download the file
              key: uploadParams.Key,
              // File type
              contentType: uploadParams.ContentType,
              // File URL
              url: data.Location,
              // Sender's user ID
              sender: userId,
            };
            // Use Signaling 2.x to send the file message payload
            rtm.publish(channelName, JSON.stringify(imageMessagePayload), {
              customType: "File",
              channelType: "MESSAGE",
            });
          }
        });
      }
    } else {
      console.log("Document pick cancelled");
    }
  } catch (err) {
    console.error("Error picking document:", err);
  }
};

Information

When using Amazon S3 for static file storage, set the correct user permissions and access policies in the Amazon S3 console. For more details, see Access Control Best Practices.

User presence and custom status

In Signaling 1.x, you can subscribe to or query the online status of multiple users, query the number of users in a channel, or obtain the list of online members in a channel. Signaling 2.x not only retains these features, but also implements upgrades and extends them. In Signaling 2.x, these capabilities are redefined and implemented in the Presence module.

Presence features require Signaling SDK version 2.2.0 or later.

Presence provides the ability to monitor user online, user offline and user status change notifications. Through the Presence module, you can obtain the following information in real time:

  • A user joins or leaves the specified channel
  • Customize temporary user status and its changes
  • Query which channels a specified user has joined or is subscribed to
  • Query which users have joined the specified channel and their temporary status data

Call the whoNow method to query the number of online users in the specified channel, the list of online users, and the temporary status of online users in real-time.

// 2.x
const options = {
  includedUserId : true,
  includedState : true,
  page : "yourBookMark"
}
try {
  const result = await rtm.presence.whoNow( "chat_room", "MESSAGE", options );
  console.log(result);
} catch (status) {
  console.log(status);
}

Call the whereNow method to get a list of channels where a specified user is present in real-time.

// 2.x
try {
  const result = await rtm.presence.whereNow( "Tony" );
  console.log(result);
} catch (status) {
  console.log(status);
}

To meet the needs of business use-cases for user status settings, 2.x provides the ability to set temporary user status. You can customize temporary user status using the setState method. Users can share custom statuses such as score, game status, location, mood, and mic status.

// 2.x
var newState = { "mood":"pumped", "isTyping":false};

try {
  const result = await rtm.Presence.setState("chat_room", "MESSAGE", newState);
  console.log(result);
} catch (status) {
  console.log(status);
}

You can also obtain a user's online status at any time using the getState method, or delete a user's status using the removeState method. After a user's temporary status changes, Signaling triggers a REMOTE_STATE_CHANGED type presence event notification. For details, see User status management.

In Signaling 2.x, listening for notifications about users joining, leaving, timing out, or changing temporary status in a channel is more convenient. You only need to follow these steps:

  1. Implement a Presence event listener
  2. When joining a channel, enable the withPresence switch
 // 2.x
 // Implement a Presence event listener
rtm.addEventListener("presence", event => {
  const action = event.eventType;
  const channelType = event.channelType;
  const channelName = event.channelName;
  const publisher = event.publisher;
  const states = event.stateChanged;
  const interval = event.interval;
  const snapshot = event.snapshot;
 // Your implementation code
});

// When subscribing to a channel, enable the withPresence switch
const options ={ withPresence : true };
try {
  const result = await rtm.subscribe("chat_room", options);
  console.log(result);
} catch (status) {
  console.log(status);
}

Signaling 2.x introduces a new design for real-time notifications. There are two modes to deliver Presence event notifications to subscribed users in a channel:

  • Real-Time Notification Mode (Announce)
  • Scheduled Notification Mode (Interval)

You can set the condition for switching between these two modes using the Announce Max size in the project's console settings. The Scheduled Notification Mode helps prevent event noise caused by too many online users in a channel. For further details, see Event Notification Modes.

User metadata and channel metadata

Based on the user and channel attributes functionality in 1.x, Signaling 2.x adds capabilities such as version control and lock control, and optimizes the API interface to make the usage simpler. In Signaling 2.x, user attributes and channel attributes are mounted under the Storage module. The following sample demonstrates how to set channel attributes:

// 2.x
const properties = {
  key : "Quantity",
  value : "20"
};

const announcement = {
  key : "Announcement",
  value : "Welcome to our shop!"
};

const price = {
  key : "T-shirt",
  value : "100"
};

const data = [properties, announcement, price];
const options = { addTimeStamp : true, addUserId : true };

try {
  const result = await rtm.storage.setChannelMetadata("channel1", "MESSAGE", data, options);
  console.log(JSON.stringify(result));
} catch (status) {
  console.log(JSON.stringify(status));
}

For information on how to obtain, update, and delete channel attributes, and how to use CAS control and lock control, please refer to Store channel metadata. The use of user attributes is similar to that of channel attributes, see Store user metadata for details.

Signaling 2.x distributes channel attributes and user attributes to users through event notifications of the storage type. To listen for storage event notifications:

  1. Implement the storage event listener
  2. When joining a channel, turn on the withMetadata switch
// 2.x
// Implement a Storage event listener
rtm.addEventListener("storage", event => {
  const channelType = event.channelType;
  const channelName = event.channelName;
  const publisher = event.publisher;
  const storageType = event.storageType;
  const action = event.eventType;
  const data = event.data;
 // Your implementation code
});
 // When joining a channel, enable the withMetadata switch
const options ={ withMetadata : false };
try {
  const result = await rtm.subscribe("chat_room", options);
  console.log(result);
} catch (status) {
  console.log(status);
}

Restrict access area

Signaling supports the access area restriction function to adapt to the laws and regulations of different countries or regions. After enabling the access area restriction function, no matter which region the user uses your app in, the SDK only accesses the Agora server in the specified region. The implementation of access area restriction in Signaling 2.x is the same as for 1.x:

AgoraRTM.setArea({ areaCodes: ["GLOBAL"], excludedArea: "CHINA" })

Other new features

In addition to the functional differences between the above versions, Signaling 2.x also adds many new features. Choose and use them according to the needs of your project.