Releases: expo/expo-server-sdk-node
v3.3.0
v3.2.0
v3.0.1
v3.0.0
Version 3.0.0 of the Expo Node SDK comes with support for the new push notification receipts. The README of this repo demonstrates how to use the new API.
Breaking changes
- TypeScript: The SDK uses TypeScript instead of Flow. We've found the TypeScript ecosystem and software to be more stable and the inference to be almost as powerful as Flow's. In the interest of stability, the SDK is now written in TypeScript and the npm package includes the original TS source.
- JavaScript Modules: CommonJS-style
require()
calls, as opposed to JavaScriptimport
statements, must now explicitly import either thedefault
export or the namedExpo
export. Any of these work:const { Expo } = require('expo-server-sdk');
const { default: Expo } = require('expo-server-sdk');
import Expo from 'expo-server-sdk';
import { Expo } from 'expo-server-sdk';
- Does not work:
const Expo = require('expo-server-sdk');
- Asynchronous Push Receipts: The
sendPushNotificationsAsync
method now returns an array of push tickets instead of push receipts. The difference is that tickets specify whether Expo successfully received the notifications, and receipts specify whether Apple or Google did. A push ticket may contain error information (formatted the same as errors in push receipts) or an"id"
field with the receipt ID. Sometime later -- usually within a couple minutes but up to half an hour later -- you must retrieve the push receipts with these IDs to learn whether Apple or Google received the notifications. The push receipts will tell you if your push credentials are correctly configured, for example, and also tell you whether you must stop sending notifications to the device that generated that receipt. - Removed singular sendPushNotificationAsync: The method
sendPushNotificationAsync
has been removed because we highly recommend using the batch method,sendPushNotificationsAsync
, for efficiency. No fundamental functionality was removed but the SDK no longer provides the less efficient convenience method.
New APIs
- Expo client instances have a method named
getPushNotificationReceiptsAsync()
that accepts an array of receipt IDs and returns a promise that resolves to an object whose keys are the receipt IDs and values are the receipts. If there is no receipt for a given ID, either because it hasn't been generated yet or because it was removed after a day for being stale, the receipt ID will be absent fro the object. - There is also a new method named
chunkPushNotificationReceiptIds()
that takes an array of receipt IDs and returns an array of smaller arrays of receipt IDs. This is a convenience method to help you split up your requests for receipts into chunks under the limit imposed by the Expo server.
v2.3.2
v2.3.1
Bug fixes
chunkPushNotifications: If you have a small number of push notifications below the chunk size limit, this will now return an array of chunks. You can always consistently call this method.
New features
- Max chunk size exposed:
Expo.pushNotificationChunkSizeLimit
: This is the maximum size of a chunk of Expo push notification messages. - Added expo.isExpoPushToken(token): Just an alias for
expo.isExponentPushToken
, which will be deprecated and later removed eventually.
v2.2.0
New features
- Chunking helper:
exponent.chunkPushNotifications(messages)
: This is a simple convenience function that takes an array of push notification messages and returns an array of arrays, each of which can be passed toexponent.sendPushNotificationsAsync
. This is because Exponent limits the number of notifications you can send in a single request (implementation detail: the limit is currently 100). - Request compression: We gzip-compress request payloads (over 1KiB) so that when you send many notification messages in a single request, the request payload is considerably smaller and reaches the Exponent servers more quickly.
- Custom HTTP agent option: The
ExponentClient
constructor takes an options map (just a plain JS object) with one supported key:httpAgent
: A custom HTTP (or HTTPS) agent to use if you want more control over connection pooling, timeouts, and other networking policies.
v2.0.1
v2.0.1 of the Exponent SDK has several breaking changes, better error handling, networking-level performance improvements, and new features for push notifications.
Breaking changes
Instead of exporting several functions, the package has one default export, which is the Exponent
API client class.
isExponentPushToken
is now a static method:Exponent.isExponentPushToken(token)
- Instantiate an
Exponent
client object to make calls to the Exponent server:new Exponent()
- Use the
Exponent
instance to send a push notification:exponent.sendPushNotificationsAsync(messages)
sendPushNotificationsAsync
accepts an array of up to 100 messages. This is what an example message looks like:
{
// The push token for the app user to whom you want to send the notification
to: 'ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]',
sound: 'default',
message: 'This is a test notification',
data: {withSome: 'data'},
}
Note that the exponentPushToken
field is now called to
! See the end of these release notes for the full message object specification.
Error handling
The sendPushNotificationsAsync
async function throws an error when the entire request fails either due to networking problems or server-side errors. But when some push notifications are successfully sent and others fail (and also if all are successful), you get back an array of push receipts. Each push receipt is a small JSON object that looks like: {"status": "ok"}
on success and {"status": "error"}
on error. Push receipts for errors also contain more information and often look like this:
{
"data": [{
"status": "error",
"message": "\"ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]\" is not a registered push notification recipient",
"details": {
"error": "DeviceNotRegistered"
}
}]
}
Important: in particular, look for an details object with an error field. If present, it may be one of these values: DeviceNotRegistered
, MessageTooBig
, MessageRateExceeded
, or InvalidCredentials
. You should handle these errors like so:
DeviceNotRegistered
: the device cannot receive push notifications anymore and you should stop sending messages to the given Exponent push token.MessageTooBig
: the total notification payload was too large. On Android and iOS the total payload must be at most 4096 bytes.MessageRateExceeded
: you are sending messages too frequently to the given device. Implement exponential backoff and slowly retry sending messages.InvalidCredentials
: your iOS push certificate for your standalone app is invalid (ex: you may have revoked it)
If we couldn't deliver the message to the Android or iOS push notification service, the receipt's details may also include service-specific information. This is useful mostly for debugging and reporting possible bugs to us.
Performance improvements
The new Exponent Node SDK connects to our new server API which has lots of server-side improvements like faster response times and pooled connections to the Android and iOS push notification services. It also supports a batch API.
Instead of sending just one notification, the SDK supports sending an array of push notifications. This way when you want to send a notification to many users, you can send up to 100 per request (to send more than 100, divide your notifications into chunks).
New push notification features
We now support many more options for sending push notifications, like setting the badge count on iOS, playing a sound, setting a TTL or expiration date (if your notification is not urgent, setting these fields tells Apple or Google to hang on to your notification if they can't deliver it right away), and the priority of your notification. See these fields:
type PushMessage = {
/**
* An Exponent push token specifying the recipient of this message.
*/
to: string,
/**
* A JSON object delivered to your app. It may be up to about 4KiB; the total
* notification payload sent to Apple and Google must be at most 4KiB or else
* you will get a "Message Too Big" error.
*/
data?: Object,
/**
* The title to display in the notification. On iOS this is displayed only
* on Apple Watch.
*/
title?: string,
/**
* The message to display in the notification
*/
body?: string,
/**
* A sound to play when the recipient receives this notification. Specify
* "default" to play the device's default notification sound, or omit this
* field to play no sound.
*/
sound?: 'default' | null,
/**
* Time to Live: the number of seconds for which the message may be kept
* around for redelivery if it hasn't been delivered yet. Defaults to 0.
*
* On Android, we make a best effort to deliver messages with zero TTL
* immediately and do not throttle them
*
* This field takes precedence over `expiration` when both are specified.
*/
ttl?: number,
/**
* A timestamp since the UNIX epoch specifying when the message expires. This
* has the same effect as the `ttl` field and is just an absolute timestamp
* instead of a relative time.
*/
expiration?: number,
/**
* The delivery priority of the message. Specify "default" or omit this field
* to use the default priority on each platform, which is "normal" on Android
* and "high" on iOS.
*
* On Android, normal-priority messages won't open network connections on
* sleeping devices and their delivery may be delayed to conserve the battery.
* High-priority messages are delivered immediately if possible and may wake
* sleeping devices to open network connections, consuming energy.
*
* On iOS, normal-priority messages are sent at a time that takes into account
* power considerations for the device, and may be grouped and delivered in
* bursts. They are throttled and may not be delivered by Apple. High-priority
* messages are sent immediately. Normal priority corresponds to APNs priority
* level 5 and high priority to 10.
*/
priority?: 'default' | 'normal' | 'high',
// iOS-specific fields
/**
* Number to display in the badge on the app icon. Specify zero to clear the
* badge.
*/
badge?: number,
}