How to handle DeviceNotRegistered error using expo-server-sdk-node

Viewed 341

I built a push notification system on my backend using expo-server-sdk-node. When I want to send notifications, I lookup the expoPushToken in my database. The docs states the following error(s) should be handled:

DeviceNotRegistered: the device cannot receive push notifications anymore and you should stop sending messages to the corresponding Expo push token.

However, I am unsure how to handle this error since there are no direct pushTokens available in the error message. See the following example:

[{
    status: 'error',
    message: '"ExponentPushToken[XXXXXXXXXXXXXXX]" is not a registered push notification recipient',
    details: { error: 'DeviceNotRegistered' }
}]

This device should now be removed from my database, but to do that I need the ExponentPushToken[XXXXXXXXXXXXXXX] value. And because the notifications are sent in batches I lose the reference to the user. What is the proper way to do this?

I thought of the following two ways:

1: Just split(") and filter the value, but this depends on the error message.

2: Loop over all my pushTokens, and find where includes(originalValue) in message, but this would mean I'd have to loop over an excessive amount of users every time it fails.

Any recommendations?

2 Answers

I faced the same issue, and here's what I did.

Considering this code

for (let chunk of chunks) {
    try {
      let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
      console.log(ticketChunk);
      tickets.push(...ticketChunk);

      // If a ticket contains an error code in ticket.details.error
      // 
    } catch (error) {
      console.error(error);
    }
  }
  • Once I send a batch of notifications (100 most likely).
  • I loop through the tickets, if ticket.status === 'error' and check for ticket.details.error === 'DeviceNotRegistered' as in the code above.
  • Since the order of sent notifications is the order in which the response tickets are received.
  • Using the current index of the tickets loop, I can access the token at the same index in the chunk I sent.
for (let chunk of chunks) {
    try {
      let ticketChunk = await expo.sendPushNotificationsAsync(chunk);
      tickets.push(...ticketChunk);

      // If a ticket contains an error code in ticket.details.error
      let ticketIndex = 0;
      for (let ticket of tickets) {
          if (ticket.status === 'error' && ticket.details.error === 'DeviceNotRegistered') {
             // Get the expo token from the `chunk` using `ticketIndex`
             // Unsubscribe the token or do whatever you want to
          }

          ticketIndex++;
      }
    } catch (error) {
      console.error(error);
    }
  }

NB: The code might contain syntax errors, it's the idea I am trying to pass across. I did the same thing with php

It's not documented behaviour (at least I didn't found it in the documentation), but in the ticket error response I can see the expoPushToken in the details object. See the screenshot attached:

enter image description here

Related