Node APN

repository·master·Indexed 42 days ago

https://github.com/parse-community/node-apn

A Node.js module for interfacing with the Apple Push Notification service (APNs) using the HTTP/2 based provider API. Version 8.1.0 provides capabilities to send standard and Live Activity notifications, manage APNs channels for iOS 18/iPadOS 18, and handle broadcast notifications. It includes the apn.Provider for connection management, apn.Notification for payload configuration, and apn.MultiProvider for managing pools of HTTP/2 connections.

Tokens
10K
Snippets
30
Records
41
Agent score
52%

What's inside Node APN

  1. Configure the notification payload

    master

    You can define the JSON payload sent to the device using two different approaches:

    1. notification.payload: An object that is JSON encoded. When you set properties on notification.aps (via convenience setters), they are merged into this payload just before transmission. If payload already contains an aps property, it is replaced by the aps properties set via the notification object.

    2. notification.rawPayload: If provided, this object is encoded and transmitted exactly as-is. Using rawPayload means convenience setters (like .body or .title) will have no effect on the final JSON output.

    // Using .payload (merges with aps properties)
    let notification = new apn.Notification();
    notification.payload = {
      from: "node-apn",
      source: "web",
    };
    notification.body = "Hello, world!";
    
    // Using .rawPayload (overrides everything, ignores setters)
    let notificationRaw = new apn.Notification();
    notificationRaw.rawPayload = {
      from: "node-apn",
      source: "web",
      aps: {
        "content-available": 1
      }
    };
    notificationRaw.body = "Hello, world!"; // This will be ignored in the output
  2. Handle notification results from apn.Provider methods

    master

    When send, manageChannels, or broadcast promises resolve, they return an object with two properties: sent and failed.

    sent

    An array of device tokens or bundle identifiers that were successfully accepted by Apple.

    failed

    An array of objects describing failures. Failures fall into two categories:

    1. Rejected Notifications (Status/Reason): Apple rejected the notification (e.g., device is unregistered). The object contains device, status (HTTP code), and response.reason.

    {
      device: "834c8b48e6254e47435d74720b1d4a13e3e57d0bf318333c284c1db8ce8ddc58",
      status: 410,
      response: { reason: "Unregistered" }
    }

    2. Error Notifications (Connection Error): A network or connection-level error occurred. The object contains device and an error property (an Error object).

    {
      device: "834c8b48e6254e47435d74720b1d4a13e3e57d0bf318333c284c1db8ce8ddc58",
      error: Error // Details about the connection failure
    }
  3. Send notifications to device tokens

    master

    Once you have a Provider and a Notification object, use the provider.send() method to transmit the notification to a list of device tokens.

    provider.send(notification, deviceTokens) returns a Promise that resolves when the transmission process is complete. The resolved response object contains two arrays:

    • response.sent: An array of device tokens that successfully received the notification.
    • response.failed: An array of objects for failed deliveries. Each object contains the device token and either an error or the status and response received from the Apple API.
    provider.send(notification, deviceTokens).then( (response) => {
    	// response.sent: Array of device tokens to which the notification was sent successfully
    	// response.failed: Array of objects containing the device token (`device`) and either an `error`, or a `status` and `response` from the API
    });
  4. How to mock `apn.Client`

    master

    The apn/mock package provides a real implementation of apn.Provider but uses a mock apn.Client as the backend. By default, this mock resolves all notifications in the sent state.

    You can inspect your application's interaction with the library by replacing apn.Client.prototype.write or the entire initializer with stubs or spies.

    // Example: Replacing the write method on the prototype to intercept calls
    const apn = require('apn/mock');
    
    // If using a testing framework like Sinon, you might do something like:
    // sinon.stub(apn.Client.prototype, 'write').resolves({
    //   device: someDevice
    // });
  5. Use `apn/mock` for testing

    master

    To test applications using node-apn without triggering actual network traffic, you can use the experimental apn/mock package. This package provides an interface identical to the main apn package, including apn.Provider, apn.Notification, and apn.token.

    To use it, replace your standard import/require statement with the mock version during your test runs.

    // Instead of:
    // const apn = require('apn');
    
    // Use:
    const apn = require('apn/mock');
  6. Migrate from v1.7 to v2.0

    master

    Version 2.0 is a complete rewrite of node-apn. If you are upgrading from v1.7 or earlier, you must account for the following breaking changes:

    • Renamed Class: apn.Connection is now apn.Provider.
    • Removed Classes: apn.Feedback and apn.Device have been removed.
    • Token Handling: Device tokens are now expected to be hex-encoded strings. Use apn.token to validate tokens or convert them from a Buffer if necessary.
    • Required Fields: Every apn.Notification must now include an associated topic.
    • Method Change: The method pushNotification(notification, tokens) has been replaced by send(notification, recipients).
    • Async Behavior: The send method now returns a Promise that fulfills once all notifications have been processed.
  7. Connect through an HTTP proxy

    master

    To connect through an HTTP proxy, include a proxy object with host and port in your provider options. The provider will establish an HTTP tunnel using an HTTP CONNECT request before creating the secure connection to APNs.

    var options = {
      token: {
        key: "path/to/APNsAuthKey_XXXXXXXXXX.p8",
        keyId: "key-id",
        teamId: "developer-team-id"
      },
      proxy: {
        host: "192.168.10.92",
        port: 8080
      },
      production: false
    };
    
    const apnProvider = new apn.Provider(options);
  8. Send a Live Activity notification

    master

    Live Activity notifications require a specific topic format (<your-app-bundle-id>.push-type.liveactivity) and a pushType of liveactivity. You should also configure properties like relevanceScore, timestamp, staleDate, event, and contentState.

    let deviceToken = "a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7";
    let note = new apn.Notification();
    
    note.topic = "<your-app-bundle-id>.push-type.liveactivity";
    note.expiry = Math.floor(Date.now() / 1000) + 3600;
    note.pushType = "liveactivity";
    note.badge = 3;
    note.sound = "ping.aiff";
    note.alert = "\uD83D\uDCE7 \u2709 You have a new message";
    note.payload = {'messageFrom': 'John Appleseed'};
    note.relevanceScore = 75;
    note.timestamp = Math.floor(Date.now() / 1000);
    note.staleDate = Math.floor(Date.now() / 1000) + (8 * 3600);
    note.event = "update";
    note.contentState = {};
    
    try {
      const result = await apnProvider.send(note, deviceToken);
    } catch (error) {
      // handle error
    }
  9. Use apn.Provider to send notifications in v2.0

    master

    In v2.0, you initialize an apn.Provider with your configuration. When sending a notification, you must set the topic property on the apn.Notification object. The send method returns a Promise that resolves to a response object containing sent and failed arrays.

    • response.sent: An array of tokens that were successfully sent.
    • response.failed: An array of failure objects. If failure.error exists, it indicates a transport-level error (e.g., network issues). Otherwise, use failure.status (HTTP status code) and failure.response (JSON payload) to handle application-level errors.
    function setup() {
      let connection = new apn.Provider(configuration);
    }
    
    function sendNotification(user) {
      let note = new apn.Notification();
      note.alert = "Hello " + user.name;
      note.topic = "io.github.node-apn.test"; // topic is required
    
      connection.send(note, user.token).then((response) => {
        response.sent.forEach((token) => {
          notificationSent(user, token);
        });
        response.failed.forEach((failure) => {
          if (failure.error) {
            // A transport-level error occurred (e.g. network problem)
            notificationError(user, failure.device, failure.error);
          } else {
            // `failure.status` is the HTTP status code
            // `failure.response` is the JSON payload
            notificationFailed(user, failure.device, failure.status, failure.response);
          }
        });
      });
    }
  10. Send a standard notification

    master

    To send a notification, create an apn.Notification object, configure its properties (like alert, badge, sound, and payload), and call apnProvider.send(note, deviceToken). The send method returns a Promise.

    let deviceToken = "a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7";
    let note = new apn.Notification();
    
    note.expiry = Math.floor(Date.now() / 1000) + 3600;
    note.badge = 3;
    note.sound = "ping.aiff";
    note.alert = "\uD83D\uDCE7 \u2709 You have a new message";
    note.payload = {'messageFrom': 'John Appleseed'};
    note.topic = "<your-app-bundle-id>";
    
    try {
      const result = await apnProvider.send(note, deviceToken);
      // handle result
    } catch(error) {
      // handle error
    }
  11. Manage APNs Channels

    master

    Starting in iOS 18/iPadOS 18, you can manage push notification channels using apnProvider.manageChannels(note, bundleId, action).

    Actions:

    • create: Creates a new channel. Requires note.payload to contain {'message-storage-policy': '1', 'push-type': 'liveactivity'}.
    • read: Retrieves a specific channel. Requires note.channelId to be populated.
    • readAll: Retrieves all active channel IDs for a bundle.
    • delete: Deletes a channel. Requires note.channelId to be populated.

    Parameters:

    • note: An apn.Notification object containing required payload/channel info.
    • bundleId: Your app's bundle identifier.
    • action: The management operation to perform.
    let bundleId = "com.node.apn";
    let note = new apn.Notification();
    note.requestId = "0309F412-AA57-46A8-9AC6-B5AECA8C4594";
    note.payload = {'message-storage-policy': '1', 'push-type': 'liveactivity'};
    
    try {
      // Create a channel
      const result = await apnProvider.manageChannels(note, bundleId, 'create');
      
      // Or read all channels
      // const result = await apnProvider.manageChannels(note, bundleId, 'readAll');
    } catch (error) {
      // handle error
    }