Your server environment and FCM
The server side of Firebase Cloud Messaging consists of two components:
- The FCM backend provided by Google.
- Your app server or other trusted server environment where your server logic runs, such as Cloud Functions for Firebase or other cloud environments managed by Google.
Your app server or trusted server environment sends message requests to the FCM backend, which then routes messages to client apps running on users' devices.
Requirements for the trusted server environment
Your app server environment must meet the following criteria:
- Able to send properly formatted message requests to the FCM backend.
- Able to handle requests and resend them using exponential back-off.
- Able to securely store server authorization credentials and client registration tokens.
- For the XMPP protocol (if used), the server must be able to generate message IDs to uniquely identify each message it sends (the FCM HTTP backend generates message IDs and returns them in the response). XMPP message IDs should be unique per sender ID.
Choosing a server option
You'll need to decide on a way to interact with FCM servers: either using the Firebase Admin SDK or the raw protocols. Because of its support across popular programming languages and its convenience methods for handling authentication and authorization, the Firebase Admin SDK is the recommended method.
Options for interacting with FCM servers include the following:
- The Firebase Admin SDK, which has support for Node, Java, Python, C#, and Go.
- The FCM HTTP v1 API, which is the most up to date of the protocol options, with more secure authorization and flexible cross-platform messaging capabilities (the Firebase Admin SDK is based on this protocol and provides all of its inherent advantages). Because new features typically are added to the HTTP v1 API only, we recommend using this API for most use cases.
- The legacy HTTP protocol. New projects are strongly recommended to adopt the FCM v1 HTTP API instead of the legacy protocol.
- The legacy XMPP server protocol. New projects are strongly recommended to adopt the FCM v1 HTTP API instead of the legacy protocol.
Firebase Admin SDK for FCM
The Admin FCM API handles authenticating with the backend and facilitates sending messages and managing topic subscriptions. With the Firebase Admin SDK, you can:
- Send messages to individual devices
- Send messages to topics and condition statements that match one or more topics.
- Subscribe and unsubscribe devices to and from topics
- Construct message payloads tailored to different target platforms
The Admin Node.js SDK provides methods for sending messages to device groups.
To set up the Firebase Admin SDK, see Add the Firebase Admin SDK to Your Server. If you already have a Firebase project, start with Add the SDK. Also, make sure to enable the Cloud Messagin API in the in the Cloud Messaging settings page for your project. Then, once the Firebase Admin SDK is installed, you can start writing logic to build send requests.
FCM Server Protocols
Currently FCM provides these raw server protocols:
- FCM HTTP v1 API
- Legacy HTTP protocol
- Legacy XMPP Protocol
Your app server can use these protocols separately or in tandem. Because it is the most up-to-date and most flexible for sending messages to multiple platforms, the FCM HTTP v1 API is recommended wherever feasible. If your requirements include upstream messaging from devices to the server, you'll need to implement the XMPP protocol.
XMPP messaging differs from HTTP messaging in the following ways:
- Upstream/Downstream messages
- HTTP: Downstream only, cloud-to-device.
- XMPP: Upstream and downstream (device-to-cloud, cloud-to-device).
- Messaging (synchronous or asynchronous)
- HTTP: Synchronous. App servers send messages as HTTP POST requests and wait for a response. This mechanism is synchronous and blocks the sender from sending another message until the response is received.
- XMPP: Asynchronous. App servers send/receive messages to/from all their devices at full line speed over persistent XMPP connections. The XMPP connection server sends acknowledgment or failure notifications (in the form of special ACK and NACK JSON-encoded XMPP messages) asynchronously.
- JSON
- HTTP: JSON messages sent as HTTP POST.
- XMPP: JSON messages encapsulated in XMPP messages.
- Plain Text
- HTTP: Plain Text messages sent as HTTP POST.
- XMPP: Not supported.
- Multicast downstream send to multiple registration tokens.
- HTTP: Supported in JSON message format.
- XMPP: Not supported.
Implementing the HTTP server protocol
To send a message, the app server issues a POST request with an HTTP header and an HTTP body comprised of JSON key value pairs. For details on the header and body options, see Build App Server Send Requests
Implementing the XMPP server protocol
The JSON payload for FCM messages is similar to the HTTP protocol, with these exceptions:
- There is no support for multiple recipients.
- FCM adds the field
message_id
, which is required. This ID uniquely identifies the message in an XMPP connection. The ACK or NACK from FCM uses themessage_id
to identify a message sent from app servers to FCM. Therefore, it's important that thismessage_id
not only be unique (per sender ID), but always present. - XMPP uses the server key to authorize a persistent connection to FCM. See Authorize Send Requests for more information.
In addition to regular FCM messages, control messages are sent, indicated by
the message_type
field in the JSON object. The value can be either
'ack' or 'nack', or 'control' (see formats below). Any FCM message with an
unknown message_type
can be ignored by your server.
For each device message your app server receives from FCM, it needs to send an ACK message. It never needs to send a NACK message. If you don't send an ACK for a message, FCM resends it the next time a new XMPP connection is established, unless the message expires first.
FCM also sends an ACK or NACK for each server-to-device message. If you do not receive either, it means that the TCP connection was closed in the middle of the operation and your server needs to resend the messages. See Flow Control for details.
See the Protocol Reference for a list of all the message parameters.
Request format
Message with payload — notification message
Here is an XMPP stanza for a notification message:
<message id=""> <gcm xmlns="google:mobile:data"> { "to":"REGISTRATION_ID", // "to" replaces "registration_ids" "notification": { "title": "Portugal vs. Denmark”, "body”: "5 to 1” }, "time_to_live":"600" } </gcm> </message>
Message with payload — data message
Here is an XMPP stanza containing the JSON message from an app server to FCM:
<message id=""> <gcm xmlns="google:mobile:data"> { "to":"REGISTRATION_ID", // "to" replaces "registration_ids" "message_id":"m-1366082849205" // new required field "data": { "hello":"world", } "time_to_live":"600", } </gcm> </message>
Response format
An FCM response can have three possible forms. The first one is a regular 'ack' message. But when the response contains an error, there are 2 different forms the message can take, described below.
ACK message
Here is an XMPP stanza containing the ACK/NACK message from FCM to the app server:
<message id=""> <gcm xmlns="google:mobile:data"> { "from":"REGID", "message_id":"m-1366082849205" "message_type":"ack" } </gcm> </message>
NACK message
A NACK error is a regular XMPP message in which the message_type
status
message is "nack". A NACK message contains:
- A NACK error code.
- A NACK error description.
Below are some examples.
Bad registration:
<message> <gcm xmlns="google:mobile:data"> { "message_type":"nack", "message_id":"msgId1", "from":"SomeInvalidRegistrationToken", "error":"BAD_REGISTRATION", "error_description":"Invalid token on 'to' field: SomeInvalidRegistrationId" } </gcm> </message>
Invalid JSON:
<message> <gcm xmlns="google:mobile:data"> { "message_type":"nack", "message_id":"msgId1", "from":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...", "error":"INVALID_JSON", "error_description":"InvalidJson: JSON_TYPE_ERROR : Field \"time_to_live\" must be a JSON java.lang.Number: abc" } </gcm> </message>
Device Message Rate Exceeded:
<message id="..."> <gcm xmlns="google:mobile:data"> { "message_type":"nack", "message_id":"msgId1", "from":"REGID", "error":"DEVICE_MESSAGE_RATE_EXCEEDED", "error_description":"Downstream message rate exceeded for this registration id" } </gcm> </message>
See the Server Reference for a complete list of the
NACK error codes. Unless otherwise
indicated, a NACKed message should not be retried. Unexpected NACK error codes
should be treated the same as INTERNAL_SERVER_ERROR
.
Stanza error
You can also get a stanza error in certain cases. A stanza error contains:
- Stanza error code.
- Stanza error description (free text).
For example:
<message id="3" type="error" to="123456789@fcm.googleapis.com/ABC"> <gcm xmlns="google:mobile:data"> {"random": "text"} </gcm> <error code="400" type="modify"> <bad-request xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"/> <text xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"> InvalidJson: JSON_PARSING_ERROR : Missing Required Field: message_id\n </text> </error> </message>
Control messages
Periodically, FCM needs to close down a connection to perform load balancing. Before it
closes the connection, FCM sends a CONNECTION_DRAINING
message to indicate that the connection is being drained
and will be closed soon. "Draining" refers to shutting off the flow of messages coming into a
connection, but allowing whatever is already in the pipeline to continue. When you receive
a CONNECTION_DRAINING
message, you should immediately begin sending messages to another FCM
connection, opening a new connection if necessary. You should, however, keep the original
connection open and continue receiving messages that may come over the connection (and
ACKing them)—FCM handles initiating a connection close when it is ready.
The CONNECTION_DRAINING
message looks like this:
<message> <data:gcm xmlns:data="google:mobile:data"> { "message_type":"control" "control_type":"CONNECTION_DRAINING" } </data:gcm> </message>
CONNECTION_DRAINING
is currently the only control_type
supported.
Flow control
Every message sent to FCM receives either an ACK or a NACK response. Messages that haven't received one of these responses are considered pending. If the pending message count reaches 100, the app server should stop sending new messages and wait for FCM to acknowledge some of the existing pending messages as illustrated in figure 1:
Figure 1. Message/ack flow.
Conversely, to avoid overloading the app server, FCM stops sending if there are too many unacknowledged messages. Therefore, the app server should "ACK" upstream messages, received from the client application via FCM, as soon as possible to maintain a constant flow of incoming messages. The aforementioned pending message limit doesn't apply to these ACKs. Even if the pending message count reaches 100, the app server should continue sending ACKs for messages received from FCM to avoid blocking delivery of new upstream messages.
ACKs are only valid within the context of one connection. If the connection is closed before a message can be ACKed, the app server should wait for FCM to resend the upstream message before ACKing it again. Similarly, all pending messages for which an ACK/NACK was not received from FCM before the connection was closed should be sent again.