CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-apn

An interface to the Apple Push Notification service for Node.js

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

APN (Apple Push Notification)

APN is a Node.js interface to the Apple Push Notification service, providing reliable delivery of push notifications to iOS devices using Apple's HTTP/2-based provider API. It features automatic connection management with connection pooling, retry mechanisms for failed notifications, and support for both token-based and certificate-based authentication.

Package Information

  • Package Name: apn
  • Package Type: npm
  • Language: JavaScript/TypeScript
  • Installation: npm install apn

Core Imports

const apn = require("apn");

ES6/TypeScript:

import * as apn from "apn";
// or destructured
import { Provider, Notification, token } from "apn";

Basic Usage

const apn = require("apn");

// Create provider with token-based authentication
const provider = new apn.Provider({
  token: {
    key: "path/to/APNsAuthKey_XXXXXXXXXX.p8",
    keyId: "key-id",
    teamId: "developer-team-id"
  },
  production: false // Use sandbox environment
});

// Create notification
const notification = new apn.Notification();
notification.expiry = Math.floor(Date.now() / 1000) + 3600; // Expires in 1 hour
notification.badge = 3;
notification.sound = "ping.aiff";
notification.alert = "📧 ✉ You have a new message";
notification.payload = { messageFrom: "John Appleseed" };
notification.topic = "com.example.MyApp"; // Your app bundle ID

// Send notification
const deviceToken = "a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7";
provider.send(notification, deviceToken).then((result) => {
  console.log("Sent:", result.sent.length);
  console.log("Failed:", result.failed.length);
  
  // Log any failures
  result.failed.forEach((failure) => {
    console.error("Failed to send to", failure.device, ":", failure.error || failure.response?.reason);
  });
});

// Shutdown when done
provider.shutdown();

Architecture

The APN package is built around three core components:

  • Provider: Main interface that manages connections to Apple's servers and handles notification delivery
  • Notification: Data structure representing a push notification with all its properties and methods
  • Token Utility: Helper function for validating and normalizing device tokens
  • Internal Systems: Credential management, HTTP/2 protocol handling, and connection pooling

Capabilities

Provider Management

The Provider class manages connections to Apple Push Notification service and handles notification delivery.

class Provider extends EventEmitter {
  constructor(options: ProviderOptions);
  send(notification: Notification, recipients: string | string[]): Promise<Responses>;
  shutdown(): void;
}

interface ProviderOptions {
  token?: ProviderToken;
  cert?: string | Buffer;
  key?: string | Buffer;
  ca?: (string | Buffer)[];
  pfx?: string | Buffer;
  passphrase?: string;
  production?: boolean;
  rejectUnauthorized?: boolean;
  connectionRetryLimit?: number;
}

interface ProviderToken {
  key: Buffer | string;
  keyId: string;
  teamId: string;
}

interface Responses {
  sent: ResponseSent[];
  failed: ResponseFailure[];
}

interface ResponseSent {
  device: string;
}

interface ResponseFailure {
  device: string;
  error?: Error;
  status?: string;
  response?: {
    reason: string;
    timestamp?: string;
  };
}

Usage Examples:

// Token-based authentication (recommended)
const provider = new apn.Provider({
  token: {
    key: "path/to/APNsAuthKey_XXXXXXXXXX.p8", // or Buffer with key data
    keyId: "ABCDEFGHIJ",
    teamId: "TEAMID1234"
  },
  production: true
});

// Certificate-based authentication
const provider = new apn.Provider({
  cert: "path/to/cert.pem", // or Buffer with certificate data
  key: "path/to/key.pem",   // or Buffer with key data
  passphrase: "keypassword", // if key is encrypted
  production: true
});

// PKCS12 format
const provider = new apn.Provider({
  pfx: "path/to/cert.p12", // or Buffer with PKCS12 data
  passphrase: "p12password",
  production: true
});

// Send to multiple devices
const deviceTokens = ["token1", "token2", "token3"];
provider.send(notification, deviceTokens).then((result) => {
  console.log(`Successfully sent to ${result.sent.length} devices`);
  console.log(`Failed to send to ${result.failed.length} devices`);
});

Notification Creation

The Notification class represents a push notification with all its properties and serialization methods.

class Notification {
  constructor(payload?: any);
  
  // Core properties
  topic: string;
  id: string;
  expiry: number;
  priority: number;
  collapseId: string;
  threadId: string;
  payload: any;
  rawPayload: any;
  
  // APS properties
  aps: Aps;
  badge: number;
  sound: string;
  alert: string | NotificationAlertOptions;
  contentAvailable: boolean;
  mutableContent: boolean;
  mdm: string | Object;
  urlArgs: string[];
  category: string;
  
  // Methods
  headers(): Object;
  compile(): string;
  length(): number;
  toJSON(): Object;
  
  // Setter methods (return this for chaining)
  setPayload(value: any): this;
  setExpiry(value: number): this;
  setPriority(value: number): this;
  setAlert(value: string | NotificationAlertOptions): this;
  setBody(value: string): this;
  setTitle(value: string): this;
  setSubtitle(value: string): this;
  setBadge(value: number): this;
  setSound(value: string): this;
  setContentAvailable(value: boolean): this;
  setMutableContent(value: boolean): this;
  setMdm(value: string | Object): this;
  setUrlArgs(value: string[]): this;
  setCategory(value: string): this;
  setThreadId(value: string): this;
  setLocKey(value: string): this;
  setLocArgs(value: string[]): this;
  setTitleLocKey(value: string): this;
  setTitleLocArgs(value: string[]): this;
  setAction(value: string): this;
  setActionLocKey(value: string): this;
  setLaunchImage(value: string): this;
}

interface NotificationAlertOptions {
  title?: string;
  subtitle?: string;
  body: string;
  "title-loc-key"?: string;
  "title-loc-args"?: string[];
  "action-loc-key"?: string;
  "loc-key"?: string;
  "loc-args"?: string[];
  "launch-image"?: string;
}

interface Aps {
  alert?: string | ApsAlert;
  "launch-image"?: string;
  badge?: number;
  sound?: string;
  "content-available"?: undefined | 1;
  "mutable-content"?: undefined | 1;
  "url-args"?: string[];
  category?: string;
  "thread-id"?: string;
}

interface ApsAlert {
  body?: string;
  "loc-key"?: string;
  "loc-args"?: any[];
  title?: string;
  "title-loc-key"?: string;
  "title-loc-args"?: any[];
  action?: string;
  "action-loc-key"?: string;
}

Usage Examples:

// Basic notification
const notification = new apn.Notification();
notification.topic = "com.example.MyApp";
notification.alert = "Hello World!";
notification.badge = 1;
notification.sound = "default";

// Complex alert with localization
notification.alert = {
  title: "New Message",
  subtitle: "From John",
  body: "Hey there! How are you?",
  "loc-key": "MESSAGE_BODY",
  "loc-args": ["John", "Hey there! How are you?"]
};

// Method chaining
const notification = new apn.Notification()
  .setTopic("com.example.MyApp")
  .setAlert("Hello World!")
  .setBadge(1)
  .setSound("default")
  .setPayload({ customData: "value" });

// Silent notification for background refresh
const silentNotification = new apn.Notification();
silentNotification.topic = "com.example.MyApp";
silentNotification.contentAvailable = true;
silentNotification.payload = { updateType: "background-sync" };

// Notification with expiry and priority
notification.expiry = Math.floor(Date.now() / 1000) + (24 * 60 * 60); // 24 hours
notification.priority = 5; // Power-conserving delivery

// Custom payload with raw JSON
notification.rawPayload = {
  aps: {
    alert: "Custom notification",
    badge: 1
  },
  customField: "customValue",
  deepLink: "myapp://profile/123"
};

Device Token Validation

Utility function for validating and normalizing device tokens received from iOS devices.

/**
 * Validates and normalizes a device token
 * @param input - Device token as string or Buffer
 * @returns Normalized hex string token
 * @throws Error if token has invalid length after cleanup
 */
function token(input: string | Buffer): string;

Usage Examples:

const apn = require("apn");

// Validate string token
const validToken = apn.token("a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7");
console.log(validToken); // Clean hex string

// Validate Buffer token
const bufferToken = Buffer.from("a9d0ed10e9cfd022a61cb08753f49c5a0b0dfb383697bf9f9d750a1003da19c7", "hex");
const validatedToken = apn.token(bufferToken);

// Handle validation errors
try {
  const cleanToken = apn.token("invalid!token@with#special$chars");
} catch (error) {
  console.error("Invalid token:", error.message);
}

// Clean up token with spaces and other characters
const messyToken = "a9d0 ed10 e9cf-d022 a61c b087";
const cleanToken = apn.token(messyToken); // Returns clean hex string

Error Handling

The APN package provides comprehensive error handling for common scenarios:

// Handle provider send failures
provider.send(notification, deviceTokens).then((result) => {
  result.failed.forEach((failure) => {
    console.error(`Failed to send to ${failure.device}:`);
    
    if (failure.error) {
      // Network or connection error
      console.error("Error:", failure.error.message);
    }
    
    if (failure.response) {
      // APNS returned an error
      console.error("APNS Error:", failure.response.reason);
      
      // Handle specific error reasons
      switch (failure.response.reason) {
        case "BadDeviceToken":
          // Remove invalid token from database
          break;
        case "Unregistered":
          // Device uninstalled app, remove token
          break;
        case "PayloadTooLarge":
          // Notification payload exceeds 4KB limit
          break;
        case "TooManyProviderTokenUpdates":
          // Rate limited, retry later
          break;
      }
    }
  });
});

// Handle token validation errors
try {
  const validToken = apn.token(userInputToken);
} catch (error) {
  console.error("Invalid device token format");
}

Common Configuration Patterns

// Development configuration
const devProvider = new apn.Provider({
  token: {
    key: process.env.APN_KEY_PATH,
    keyId: process.env.APN_KEY_ID,
    teamId: process.env.APN_TEAM_ID
  },
  production: false
});

// Production configuration
const prodProvider = new apn.Provider({
  token: {
    key: fs.readFileSync(process.env.APN_KEY_PATH),
    keyId: process.env.APN_KEY_ID,
    teamId: process.env.APN_TEAM_ID
  },
  production: true
});

// Enterprise configuration with custom CA
const enterpriseProvider = new apn.Provider({
  cert: process.env.APN_CERT_PATH,
  key: process.env.APN_KEY_PATH,
  ca: [fs.readFileSync("corporate-ca.pem")],
  production: true,
  connectionRetryLimit: 5
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/apn@2.2.x
Publish Source
CLI
Badge
tessl/npm-apn badge