Hex.pmAPI DocsBuild Status

Express

Library for sending push notifications. Supports Apple APNS and Google FCM services.

At the moment sends pushes to FCM via HTTP and to APNS via HTTP/2 (with either certificate or JWT).

Uses GenServer in order to balance the load. Default buffer (producer) size is 5000. Default consumer max demand is number_of_available_schedulers * 5 (multiplier can be adjusted).

Installation

# in your mix.exs file

def deps do
  {:express, "~> 1.1.2"}
end

# in your config.exs file (more in configuration section below)

config :express,
       apns: [
         mode: :prod,
         cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
         key_path: System.get_env("EXPRESS_APNS_KEY_PATH")
       ],
       fcm: [
         api_key: System.get_env("EXPRESS_FCM_API_KEY")
       ]

Quick examples

APNS

alias Express.APNS

push_message =
  %APNS.PushMessage{
    token: "your_device_token",
    topic: "your_app_topic",
    acme: %{},
    aps: %APNS.PushMessage.Aps{
      badge: 1,
      content_available: 1,
      alert: %APNS.PushMessage.Alert{
        title: "Hello",
        body: "World"
      }
    }
  }

opts = [delay: 5] # in seconds

callback_fun =
  fn(push_message, response) ->
    IO.inspect("==Push message==")
    IO.inspect(push_message)
    IO.inspect("==APNS response==")
    IO.inspect(response)
  end

APNS.push(push_message, opts, callback_fun)

FCM

alias Express.FCM

push_message =
  %FCM.PushMessage{
    to: "your_device_registration_id",
    priority: "high",
    content_available: true,
    data: %{},
    notification: %FCM.PushMessage.Notification{
      title: "Hello",
      body: "World"
    }
  }

opts = [delay: 5] # in seconds

callback_fun =
  fn(push_message, response) ->
    IO.inspect("==Push message==")
    IO.inspect(push_message)
    IO.inspect("==FCM response==")
    IO.inspect(response)
  end

FCM.push(push_message, opts, callback_fun)

Configuration

Most of options can be changed in config file, but try to start with defaults. Every config option (except basics) has a default value.

Basic

config :express,
       apns: [
         mode: :prod,
         cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
         key_path: System.get_env("EXPRESS_APNS_KEY_PATH")
       ],
       fcm: [
         api_key: System.get_env("EXPRESS_FCM_API_KEY")
       ]

There is an option:

for APNS you can provide also cert and key options (values/content of your certificate and key files respectively) the priority of the options is: cert_path > cert and key_path > key

Buffer

There are all possible options for the buffer:

config :express,
       buffer: [
         max_size: 5000,
         consumers_count: 5,
         consumer_demand_multiplier: 5,
         adders_pool_config: [
           {:name, {:local, :buffer_adders_pool}},
           {:worker_module, Express.PushRequests.Adder},
           {:size, 5},
           {:max_overflow, 1}
         ]
       ]

APNS

Possible options for APNS:

you should provide either (cert_path & key_path) or (cert & key) or (key_id & team_id & auth_key_path)

config :express,
       apns: [
         mode: :prod,
         # for requests with jwt
         key_id: System.get_env("EXPRESS_APNS_KEY_ID"),
         team_id: System.get_env("EXPRESS_APNS_TEAM_ID"),
         auth_key_path: System.get_env("EXPRESS_APNS_AUTH_KEY_PATH"),

         # for requests with a certificate
         cert_path: System.get_env("EXPRESS_APNS_CERT_PATH"),
         key_path: System.get_env("EXPRESS_APNS_KEY_PATH"),

         # workers config (if default doesn't meet you requirements)
         workers_pool_config: [
           {:name, {:local, :apns_workers_pool}},
           {:worker_module, Express.APNS.Worker},
           {:size, 8},
           {:max_overflow, 1}
         ]
       ]

FCM

Possible options for FCM:

config :express,
       fcm: [
         api_key: System.get_env("EXPRESS_FCM_API_KEY")

         # workers config (if default doesn't meet you requirements)
         workers_pool_config: [
           {:name, {:local, :fcm_workers_pool}},
           {:worker_module, Express.FCM.Worker},
           {:size, 8},
           {:max_overflow, 1}
         ]
       ]

Push message structure

You should construct %Express.APNS.PushMessage{} and %Express.FCM.PushMessage{} structures and pass them to Express.APNS.push/3 and Express.FCM.push/3 respectively in order to send a push message.

Express’s Express.APNS.PushMessage as well as Express.FCM.PushMessage conforms official Apple & Google push message structures, so there should not be any confusion with it.

Here are their structures:

APNS

%Express.APNS.PushMessage{
  token: String.t,
  topic: String.t,
  aps: Express.APNS.PushMessage.Aps.t,
  apple_watch: map(),
  acme: map()
}

%Express.APNS.PushMessage.Aps{
  content_available: pos_integer(),
  mutable_content: pos_integer(),
  badge: pos_integer(),
  sound: String.t,
  category: String.t,
  thread_id: String.t,
  alert: Express.APNS.PushMessage.Alert.t | String.t
}

%Express.APNS.PushMessage.Alert{
  title: String.t,
  body: String.t
}

FCM

%Express.FCM.PushMessage{
  to: String.t,
  registration_ids: [String.t],
  priority: String.t,
  content_available: boolean(),
  collapse_key: String.t,
  data: map(),
  notification: PushMessage.Notification.t
}

%Express.FCM.PushMessage.Notification{
  title: String.t,
  body: String.t,
  icon: String.t,
  sound: String.t,
  click_action: String.t,
  badge: pos_integer(),
  category: String.t
}

Send a push message

In order to send a push message you should to construct a valid message structure, define a callback function, which will be invoked on provider’s response (APNS or FCM) and pass them along with options to either Express.FCM.push/3 or Express.APNS.push/3 function (see quick examples above).

Nothing to add here, but:

# push result type
@type push_result :: {:ok, %{status: pos_integer(), body: any()}} |
                     {:error, %{status: pos_integer(), body: any()}}

Supervision tree

                                     Application
                                          |
                                    Supervisor 
                                          |
          -----------------------------------------------------------------------
          |                    |                        |                       |
   APNS.Supervisor      FCM.Supervisor       PushRequests.Supervisor      TasksSupervisor
          |                    |                                 |              |
          |         -------------------------                    |   ------------------------
          |         |                       |                    |   |          |           |
          |  FCM.DelayedPushes      :fcm_workers_pool            |  Task       Task        Task
          |                                 |                    |
          |                     ------------------------         |
          |                     |           |          |         |
          |                 FCM.Worker  FCM.Worker  FCM.Worker   |
          |                                                      |
          |                               ----------------------------------------
          |                               |               |                      |
          |                     PushRequests.Buffer   :buffer_adders_pool   PushRequests.ConsumersSupervisor
          |                                               |                      |
          |                                        -------------         ----------------
          |                                        |           |         |              |
          |                                        |           |PushRequests.Consumer  PushRequests.Consumer
          |                                        |           |
          |                               PushRequests.Adder  PushRequests.Adder
          |
      ---------------------------------------------
      |                      |                    |
APNS.JWTHolder      APNS.DelayedPushes    :apns_workers_pool
                                                  |
                                         --------------------------------------
                                         |                |                   |
                                    APNS.Worker      APNS.Worker         APNS.Worker
                                         |                |                   |
                                  APNS.Connection   APNS.Connection    APNS.Connection

LICENSE

Copyright © 2017 Andrey Chernykh ( andrei.chernykh@gmail.com )

This work is free. You can redistribute it and/or modify it under the
terms of the MIT License. See the LICENSE file for more details.